[flang-commits] [flang] [flang] only instantiate required symbols from parent modules (PR #193689)
via flang-commits
flang-commits at lists.llvm.org
Fri Apr 24 00:56:26 PDT 2026
https://github.com/jeanPerier updated https://github.com/llvm/llvm-project/pull/193689
>From 34c8db41f36537ceed2b69ba0de8a183248e69a4 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Wed, 22 Apr 2026 08:12:17 -0700
Subject: [PATCH 1/2] [flang] only instantiate required symbols from parent
modules
---
flang/include/flang/Lower/PFTBuilder.h | 7 ++
flang/include/flang/Semantics/tools.h | 3 +
flang/lib/Lower/Bridge.cpp | 27 ++++----
flang/lib/Lower/PFTBuilder.cpp | 69 +++++++++++++++++++
flang/lib/Semantics/tools.cpp | 2 +-
.../test/Lower/OpenMP/target-map-complex.f90 | 2 +-
flang/test/Lower/c-interoperability.f90 | 2 -
.../host_module_variable_instantiation.f90 | 19 +++++
.../Lower/proc_pointer_hidden_by_generic.f90 | 33 +++++++++
9 files changed, 148 insertions(+), 16 deletions(-)
create mode 100644 flang/test/Lower/host_module_variable_instantiation.f90
create mode 100644 flang/test/Lower/proc_pointer_hidden_by_generic.f90
diff --git a/flang/include/flang/Lower/PFTBuilder.h b/flang/include/flang/Lower/PFTBuilder.h
index 55a755acaaeb7..8da101a11e47e 100644
--- a/flang/include/flang/Lower/PFTBuilder.h
+++ b/flang/include/flang/Lower/PFTBuilder.h
@@ -608,6 +608,13 @@ VariableList getScopeVariableList(const Fortran::semantics::Scope &scope);
/// depends on. \p symbol itself will be the last variable in the list.
VariableList getDependentVariableList(const Fortran::semantics::Symbol &);
+struct FunctionLikeUnit;
+/// Create an ordered list of equivalence sets and variables from host
+/// [sub]module scopes of \p funit that are referenced in \p funit. This is
+/// used by lowering of module procedures (and their internal subprograms) to
+/// only instantiate referenced host module variables rather than all of them.
+VariableList getHostModuleVariableList(const FunctionLikeUnit &funit);
+
void dump(VariableList &, std::string s = {}); // `s` is an optional dump label
/// Function-like units may contain evaluations (executable statements),
diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h
index 9f77d0ec5da2e..53248a2b358d6 100644
--- a/flang/include/flang/Semantics/tools.h
+++ b/flang/include/flang/Semantics/tools.h
@@ -127,6 +127,9 @@ bool IsSeparateModuleProcedureInterface(const Symbol *);
bool HasAlternateReturns(const Symbol &);
bool IsAutomaticallyDestroyed(const Symbol &);
+// Follow association until the first symbol without HostAssocDetails.
+const Symbol &FollowHostAssoc(const Symbol &);
+
// Return an ultimate component of type that matches predicate, or nullptr.
const Symbol *FindUltimateComponent(const DerivedTypeSpec &type,
const std::function<bool(const Symbol &)> &predicate);
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 655edc8ffa7b3..4594a2f73426e 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -6592,18 +6592,21 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::lower::AggregateStoreMap storeMap;
- // Map all containing submodule and module equivalences and variables, in
- // case they are referenced. It might be better to limit this to variables
- // that are actually referenced, although that is more complicated when
- // there are equivalenced variables.
- auto &scopeVariableListMap =
- Fortran::lower::pft::getScopeVariableListMap(funit);
- for (auto *scp = &scope.parent(); !scp->IsGlobal(); scp = &scp->parent())
- if (scp->kind() == Fortran::semantics::Scope::Kind::Module)
- for (const auto &var : Fortran::lower::pft::getScopeVariableList(
- *scp, scopeVariableListMap))
- if (!var.isRuntimeTypeInfoData())
- instantiateVar(var, storeMap);
+ // Map containing [sub]module equivalences and variables that are
+ // referenced in this function-like unit. Only referenced host module
+ // variables are instantiated to avoid generating IR for unused module
+ // variables. Equivalenced variables are handled via the aggregate store
+ // analysis performed on the host [sub]module scopes.
+ const bool hasHostModuleScope = [&]() {
+ for (auto *scp = &scope.parent(); !scp->IsGlobal(); scp = &scp->parent())
+ if (scp->kind() == Fortran::semantics::Scope::Kind::Module)
+ return true;
+ return false;
+ }();
+ if (hasHostModuleScope)
+ for (const auto &var :
+ Fortran::lower::pft::getHostModuleVariableList(funit))
+ instantiateVar(var, storeMap);
// Map function equivalences and variables.
mlir::Value primaryFuncResultStorage;
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 437e70265de94..d1b689edbb6a5 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -1622,6 +1622,20 @@ struct SymbolDependenceAnalysis {
analyze(symbol);
finalize();
}
+ /// Analyze the dependencies of a set of module variables that are host
+ /// associated (or use associated in host module scopes).
+ SymbolDependenceAnalysis(
+ const llvm::SetVector<const semantics::Symbol *> &moduleVariables) {
+ for (const semantics::Symbol *sym : moduleVariables)
+ analyzeLocalEquivalenceSets(sym->owner());
+ // Add all aggregate stores to the front of the variable list.
+ adjustSize(1);
+ for (auto st : stores)
+ layeredVarList[0].emplace_back(std::move(st));
+ for (const semantics::Symbol *sym : moduleVariables)
+ analyze(*sym);
+ finalize();
+ }
Fortran::lower::pft::VariableList getVariableList() {
return std::move(layeredVarList[0]);
}
@@ -2131,6 +2145,61 @@ lower::pft::getDependentVariableList(const semantics::Symbol &symbol) {
return sda.getVariableList();
}
+static bool
+isGenericHiddingProcedurePointer(const Fortran::semantics::Symbol &sym) {
+ if (const auto *generic = sym.detailsIf<Fortran::semantics::GenericDetails>())
+ if (const Fortran::semantics::Symbol *specific = generic->specific())
+ return Fortran::semantics::IsProcedurePointer(*specific);
+ return false;
+}
+
+/// Collect the canonical list of host [sub]module variables referenced in \p
+/// funit. A symbol is considered a host module variable if it is an
+/// ObjectEntityDetails, a ProcedurePointer, or a NamelistDetails symbol whose
+/// ultimate owning scope is a [sub]module scope containing \p funit's scope.
+/// Namelist groups are expanded to the set of their objects.
+static void collectHostAssociatedModuleVariables(
+ const Fortran::lower::pft::FunctionLikeUnit &funit,
+ llvm::SetVector<const Fortran::semantics::Symbol *> &moduleVariables) {
+ auto addIfHostModuleVariable = [&](const Fortran::semantics::Symbol &sym) {
+ const Fortran::semantics::Symbol &ultimate = sym.GetUltimate();
+ const auto *namelistDetails =
+ ultimate.detailsIf<Fortran::semantics::NamelistDetails>();
+ if (!ultimate.has<Fortran::semantics::ObjectEntityDetails>() &&
+ !Fortran::semantics::IsProcedurePointer(ultimate) &&
+ !isGenericHiddingProcedurePointer(ultimate) && !namelistDetails)
+ return;
+ const Fortran::semantics::Scope &symbolScope =
+ Fortran::semantics::FollowHostAssoc(sym).owner();
+ if (symbolScope.kind() != Fortran::semantics::Scope::Kind::Module)
+ return;
+ if (namelistDetails) {
+ // Namelist symbols are processed on the fly in IO lowering, which
+ // needs to be able to access each of their objects. Capture the
+ // objects rather than the namelist symbol itself.
+ for (const auto &namelistObject : namelistDetails->objects())
+ moduleVariables.insert(&namelistObject->GetUltimate());
+ } else {
+ moduleVariables.insert(&ultimate);
+ }
+ };
+ Fortran::lower::pft::visitAllSymbols(funit, addIfHostModuleVariable);
+}
+
+/// Create an ordered list of equivalences and variables from host
+/// [sub]modules of \p funit that are referenced in \p funit. The result is
+/// not cached.
+lower::pft::VariableList
+lower::pft::getHostModuleVariableList(const FunctionLikeUnit &funit) {
+ LLVM_DEBUG(llvm::dbgs() << "\ngetHostModuleVariableList of funit scope <"
+ << &funit.getScope() << "> "
+ << funit.getScope().GetName() << "\n");
+ llvm::SetVector<const semantics::Symbol *> moduleVariables;
+ collectHostAssociatedModuleVariables(funit, moduleVariables);
+ SymbolDependenceAnalysis sda(moduleVariables);
+ return sda.getVariableList();
+}
+
namespace {
/// Helper class to find all the symbols referenced in a FunctionLikeUnit.
/// It defines a parse tree visitor doing a deep visit in all nodes with
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index e0e8727251ed8..86f0f59adc6f4 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -262,7 +262,7 @@ bool DoesScopeContain(const Scope *maybeAncestor, const Symbol &symbol) {
return DoesScopeContain(maybeAncestor, symbol.owner());
}
-static const Symbol &FollowHostAssoc(const Symbol &symbol) {
+const Symbol &FollowHostAssoc(const Symbol &symbol) {
for (const Symbol *s{&symbol};;) {
const auto *details{s->detailsIf<HostAssocDetails>()};
if (!details) {
diff --git a/flang/test/Lower/OpenMP/target-map-complex.f90 b/flang/test/Lower/OpenMP/target-map-complex.f90
index fc01bdafe51ed..2325aec79e65b 100644
--- a/flang/test/Lower/OpenMP/target-map-complex.f90
+++ b/flang/test/Lower/OpenMP/target-map-complex.f90
@@ -8,8 +8,8 @@
!CHECK-FPRIV: omp.private {type = firstprivate} @[[PRIV_32:.*]] : complex<f32> copy {
!CHECK-LABEL: func.func @_QMmPbar()
-!CHECK: %[[V0:[0-9]+]]:2 = hlfir.declare {{.*}} (!fir.ref<complex<f64>>) -> (!fir.ref<complex<f64>>, !fir.ref<complex<f64>>)
!CHECK: %[[V1:[0-9]+]]:2 = hlfir.declare {{.*}} (!fir.ref<complex<f32>>) -> (!fir.ref<complex<f32>>, !fir.ref<complex<f32>>)
+!CHECK: %[[V0:[0-9]+]]:2 = hlfir.declare {{.*}} (!fir.ref<complex<f64>>) -> (!fir.ref<complex<f64>>, !fir.ref<complex<f64>>)
!CHECK-FPRIV: %[[V2:[0-9]+]] = omp.map.info var_ptr(%[[V1]]#0 : !fir.ref<complex<f32>>, complex<f32>) {{.*}} capture(ByCopy)
!CHECK-FPRIV: %[[V3:[0-9]+]] = omp.map.info var_ptr(%[[V0]]#0 : !fir.ref<complex<f64>>, complex<f64>) {{.*}} capture(ByRef)
!CHECK-FPRIV: omp.target map_entries(%[[V2]] -> {{.*}}, %[[V3]] -> {{.*}} : !fir.ref<complex<f32>>, !fir.ref<complex<f64>>) private(@[[PRIV_32]] %[[V1]]#0 -> %{{.*}} [map_idx=0], @[[PRIV_64]] %[[V0]]#0 -> %{{.*}} [map_idx=1] : !fir.ref<complex<f32>>, !fir.ref<complex<f64>>) {
diff --git a/flang/test/Lower/c-interoperability.f90 b/flang/test/Lower/c-interoperability.f90
index 724763027763a..73886f74c69ef 100644
--- a/flang/test/Lower/c-interoperability.f90
+++ b/flang/test/Lower/c-interoperability.f90
@@ -2,8 +2,6 @@
! CHECK-LABEL: func @_QMc_interoperability_testPget_a_thing() -> !fir.type<_QMc_interoperability_testTthing_with_pointer{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}> {
! CHECK: %[[VAL_0:.*]] = fir.dummy_scope : !fir.dscope
-! CHECK: %[[VAL_1:.*]] = fir.address_of(@_QM__fortran_builtinsEC__builtin_c_null_ptr) : !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>
-! CHECK: %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_1]] {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QM__fortran_builtinsEC__builtin_c_null_ptr"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>)
! CHECK: %[[VAL_3:.*]] = fir.address_of(@_QMc_interoperability_testEthis_thing) : !fir.ref<!fir.type<_QMc_interoperability_testTthing_with_pointer{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>
! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_3]] {uniq_name = "_QMc_interoperability_testEthis_thing"} : (!fir.ref<!fir.type<_QMc_interoperability_testTthing_with_pointer{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>) -> (!fir.ref<!fir.type<_QMc_interoperability_testTthing_with_pointer{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.ref<!fir.type<_QMc_interoperability_testTthing_with_pointer{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>)
! CHECK: %[[VAL_5:.*]] = fir.alloca !fir.type<_QMc_interoperability_testTthing_with_pointer{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}> {bindc_name = "get_a_thing", uniq_name = "_QMc_interoperability_testFget_a_thingEget_a_thing"}
diff --git a/flang/test/Lower/host_module_variable_instantiation.f90 b/flang/test/Lower/host_module_variable_instantiation.f90
new file mode 100644
index 0000000000000..029b7b9329589
--- /dev/null
+++ b/flang/test/Lower/host_module_variable_instantiation.f90
@@ -0,0 +1,19 @@
+! Test that host module variables are not instantiated inside
+! module procedure when they are not needed.
+
+! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
+
+module test_host_module_instantiation
+ integer :: var1, var2
+contains
+subroutine foo()
+ call bar(var1)
+end subroutine
+end module
+
+! CHECK-LABEL: func.func @_QMtest_host_module_instantiationPfoo(
+! CHECK-NOT: fir.address_of
+! CHECK: %[[ADDR1:.*]] = fir.address_of(@_QMtest_host_module_instantiationEvar1) : !fir.ref<i32>
+! CHECK: hlfir.declare %[[ADDR1]]
+! CHECK-NOT: fir.address_of
+! CHECK: return
diff --git a/flang/test/Lower/proc_pointer_hidden_by_generic.f90 b/flang/test/Lower/proc_pointer_hidden_by_generic.f90
new file mode 100644
index 0000000000000..f8efb027fd0d2
--- /dev/null
+++ b/flang/test/Lower/proc_pointer_hidden_by_generic.f90
@@ -0,0 +1,33 @@
+! Test instantiation of procedure pointer hidden behind
+! generic interface host associated from a module.
+
+! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
+
+module m_proc_pointer_hidden_by_generic
+procedure (proc2),pointer :: p
+interface p
+ procedure proc1, p
+end interface
+contains
+subroutine proc1(i)
+ integer :: i
+end subroutine
+subroutine proc2(x)
+ real :: x
+end subroutine
+
+subroutine test()
+p=>proc2
+end subroutine
+end
+
+! CHECK-LABEL: func.func @_QMm_proc_pointer_hidden_by_genericPtest(
+! CHECK: %[[DUMMY_SCOPE_0:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[ADDRESS_OF_0:.*]] = fir.address_of(@_QMm_proc_pointer_hidden_by_genericEp) : !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> ()>>
+! CHECK: %[[DECLARE_0:.*]]:2 = hlfir.declare %[[ADDRESS_OF_0]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QMm_proc_pointer_hidden_by_genericEp"} : (!fir.ref<!fir.boxproc<(!fir.ref<f32>) -> ()>>) -> (!fir.ref<!fir.boxproc<(!fir.ref<f32>) -> ()>>, !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> ()>>)
+! CHECK: %[[ADDRESS_OF_1:.*]] = fir.address_of(@_QMm_proc_pointer_hidden_by_genericPproc2) : (!fir.ref<f32>) -> ()
+! CHECK: %[[EMBOXPROC_0:.*]] = fir.emboxproc %[[ADDRESS_OF_1]] : ((!fir.ref<f32>) -> ()) -> !fir.boxproc<() -> ()>
+! CHECK: %[[CONVERT_0:.*]] = fir.convert %[[EMBOXPROC_0]] : (!fir.boxproc<() -> ()>) -> !fir.boxproc<(!fir.ref<f32>) -> ()>
+! CHECK: fir.store %[[CONVERT_0]] to %[[DECLARE_0]]#0 : !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> ()>>
+! CHECK: return
+! CHECK: }
>From 5313e9b6f8fbe748618b7c81e59cffd202fbe95c Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 24 Apr 2026 00:55:56 -0700
Subject: [PATCH 2/2] address comments
---
flang/lib/Lower/PFTBuilder.cpp | 6 +++---
.../host_module_variable_instantiation.f90 | 17 +++++++++++++++++
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index d1b689edbb6a5..3208b5ce921e8 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -1624,7 +1624,7 @@ struct SymbolDependenceAnalysis {
}
/// Analyze the dependencies of a set of module variables that are host
/// associated (or use associated in host module scopes).
- SymbolDependenceAnalysis(
+ explicit SymbolDependenceAnalysis(
const llvm::SetVector<const semantics::Symbol *> &moduleVariables) {
for (const semantics::Symbol *sym : moduleVariables)
analyzeLocalEquivalenceSets(sym->owner());
@@ -2146,7 +2146,7 @@ lower::pft::getDependentVariableList(const semantics::Symbol &symbol) {
}
static bool
-isGenericHiddingProcedurePointer(const Fortran::semantics::Symbol &sym) {
+isGenericHidingProcedurePointer(const Fortran::semantics::Symbol &sym) {
if (const auto *generic = sym.detailsIf<Fortran::semantics::GenericDetails>())
if (const Fortran::semantics::Symbol *specific = generic->specific())
return Fortran::semantics::IsProcedurePointer(*specific);
@@ -2167,7 +2167,7 @@ static void collectHostAssociatedModuleVariables(
ultimate.detailsIf<Fortran::semantics::NamelistDetails>();
if (!ultimate.has<Fortran::semantics::ObjectEntityDetails>() &&
!Fortran::semantics::IsProcedurePointer(ultimate) &&
- !isGenericHiddingProcedurePointer(ultimate) && !namelistDetails)
+ !isGenericHidingProcedurePointer(ultimate) && !namelistDetails)
return;
const Fortran::semantics::Scope &symbolScope =
Fortran::semantics::FollowHostAssoc(sym).owner();
diff --git a/flang/test/Lower/host_module_variable_instantiation.f90 b/flang/test/Lower/host_module_variable_instantiation.f90
index 029b7b9329589..7c1460bb122b4 100644
--- a/flang/test/Lower/host_module_variable_instantiation.f90
+++ b/flang/test/Lower/host_module_variable_instantiation.f90
@@ -5,10 +5,16 @@
module test_host_module_instantiation
integer :: var1, var2
+ integer :: a, b, c
+ equivalence (a, b)
contains
subroutine foo()
call bar(var1)
end subroutine
+
+subroutine foo_equiv
+ b = 1
+end subroutine
end module
! CHECK-LABEL: func.func @_QMtest_host_module_instantiationPfoo(
@@ -17,3 +23,14 @@ subroutine foo()
! CHECK: hlfir.declare %[[ADDR1]]
! CHECK-NOT: fir.address_of
! CHECK: return
+
+
+! CHECK-LABEL: func.func @_QMtest_host_module_instantiationPfoo_equiv(
+! CHECK-NOT: hlfir.declare
+! CHECK: %[[ADDRESS_OF_0:.*]] = fir.address_of(@_QMtest_host_module_instantiationEa) : !fir.ref<!fir.array<4xi8>>
+! CHECK-NEXT: %[[CONSTANT_0:.*]] = arith.constant 0 : index
+! CHECK-NEXT: %[[COORDINATE_OF_0:.*]] = fir.coordinate_of %[[ADDRESS_OF_0]], %[[CONSTANT_0]] : (!fir.ref<!fir.array<4xi8>>, index) -> !fir.ref<i8>
+! CHECK-NEXT: %[[CONVERT_0:.*]] = fir.convert %[[COORDINATE_OF_0]] : (!fir.ref<i8>) -> !fir.ptr<i32>
+! CHECK-NEXT: %[[DECLARE_0:.*]]:2 = hlfir.declare %[[CONVERT_0]] storage(%[[ADDRESS_OF_0]][0]) {uniq_name = "_QMtest_host_module_instantiationEb"} : (!fir.ptr<i32>, !fir.ref<!fir.array<4xi8>>) -> (!fir.ptr<i32>, !fir.ptr<i32>)
+! CHECK-NOT: hlfir.declare
+! CHECK: return
More information about the flang-commits
mailing list