[flang-commits] [flang] [flang] Don't emit needless symbols to hermetic module files (PR #144765)
via flang-commits
flang-commits at lists.llvm.org
Wed Jun 18 10:44:20 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
Author: Peter Klausler (klausler)
<details>
<summary>Changes</summary>
When emitting the dependent modules for a hermetic module file, omit symbols that are not necessary (directly or otherwise) for the declarations and definitions in the main module.
(Includes https://github.com/llvm/llvm-project/pull/144618 as the first commit.)
---
Patch is 24.80 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/144765.diff
9 Files Affected:
- (added) flang/include/flang/Semantics/symbol-set-closure.h (+34)
- (modified) flang/lib/Semantics/CMakeLists.txt (+1)
- (modified) flang/lib/Semantics/mod-file.cpp (+58-97)
- (modified) flang/lib/Semantics/mod-file.h (+3-2)
- (modified) flang/lib/Semantics/resolve-names.cpp (+6-4)
- (added) flang/lib/Semantics/symbol-set-closure.cpp (+187)
- (modified) flang/lib/Semantics/tools.cpp (+3-3)
- (modified) flang/lib/Semantics/type.cpp (+11-1)
- (modified) flang/test/Semantics/modfile78.F90 (-1)
``````````diff
diff --git a/flang/include/flang/Semantics/symbol-set-closure.h b/flang/include/flang/Semantics/symbol-set-closure.h
new file mode 100644
index 0000000000000..d7f2f74c47e9a
--- /dev/null
+++ b/flang/include/flang/Semantics/symbol-set-closure.h
@@ -0,0 +1,34 @@
+//===-- include/flang/Semantics/symbol-set-closure.h ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SEMANTICS_SYMBOLS_SET_CLOSURE_H_
+#define FORTRAN_SEMANTICS_SYMBOLS_SET_CLOSURE_H_
+
+#include "flang/Semantics/symbol.h"
+
+namespace Fortran::semantics {
+
+// For a set or scope of symbols, computes the transitive closure of their
+// dependences due to their types, bounds, specific procedures, interfaces,
+// initialization, storage association, &c. Includes the original symbol
+// or members of the original set. Does not include dependences from
+// subprogram definitions, only their interfaces.
+enum DependenceCollectionFlags {
+ NoDependenceCollectionFlags = 0,
+ IncludeOriginalSymbols = 1 << 0,
+ FollowUseAssociations = 1 << 1,
+ IncludeSpecificsOfGenerics = 1 << 2,
+ IncludeComponentsInExprs = 1 << 3,
+};
+UnorderedSymbolSet CollectAllDependences(
+ const UnorderedSymbolSet &, int = NoDependenceCollectionFlags);
+UnorderedSymbolSet CollectAllDependences(
+ const Scope &, int = NoDependenceCollectionFlags);
+
+} // namespace Fortran::semantics
+#endif // FORTRAN_SEMANTICS_SYMBOLS_SET_CLOSURE_H_
diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt
index 18c89587843a9..c1be83b0c744c 100644
--- a/flang/lib/Semantics/CMakeLists.txt
+++ b/flang/lib/Semantics/CMakeLists.txt
@@ -46,6 +46,7 @@ add_flang_library(FortranSemantics
scope.cpp
semantics.cpp
symbol.cpp
+ symbol-set-closure.cpp
tools.cpp
type.cpp
unparse-with-symbols.cpp
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index 82c8536902eb2..9985c4a3cecdb 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -15,6 +15,7 @@
#include "flang/Parser/unparse.h"
#include "flang/Semantics/scope.h"
#include "flang/Semantics/semantics.h"
+#include "flang/Semantics/symbol-set-closure.h"
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
#include "llvm/Support/FileSystem.h"
@@ -47,8 +48,8 @@ struct ModHeader {
};
static std::optional<SourceName> GetSubmoduleParent(const parser::Program &);
-static void CollectSymbols(
- const Scope &, SymbolVector &, SymbolVector &, SourceOrderedSymbolSet &);
+static void CollectSymbols(const Scope &, SymbolVector &, SymbolVector &,
+ SourceOrderedSymbolSet &, UnorderedSymbolSet *);
static void PutPassName(llvm::raw_ostream &, const std::optional<SourceName> &);
static void PutInit(llvm::raw_ostream &, const Symbol &, const MaybeExpr &,
const parser::Expr *, SemanticsContext &);
@@ -130,7 +131,7 @@ static std::string ModFileName(const SourceName &name,
return ancestorName.empty() ? result : ancestorName + '-' + result;
}
-// Write the module file for symbol, which must be a module or submodule.
+// Writes the module file for symbol, which must be a module or submodule.
void ModFileWriter::Write(const Symbol &symbol) {
const auto &module{symbol.get<ModuleDetails>()};
if (symbol.test(Symbol::Flag::ModFile) || module.moduleFileHash()) {
@@ -142,21 +143,30 @@ void ModFileWriter::Write(const Symbol &symbol) {
std::string path{context_.moduleDirectory() + '/' +
ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())};
- std::set<std::string> hermeticModuleNames;
- hermeticModuleNames.insert(symbol.name().ToString());
UnorderedSymbolSet additionalModules;
+ UnorderedSymbolSet dependenceClosure;
+ if (hermeticModuleFileOutput_) {
+ dependenceClosure = CollectAllDependences(
+ DEREF(symbol.scope()), IncludeOriginalSymbols | FollowUseAssociations);
+ }
PutSymbols(DEREF(symbol.scope()),
- hermeticModuleFileOutput_ ? &additionalModules : nullptr);
+ hermeticModuleFileOutput_ ? &additionalModules : nullptr,
+ hermeticModuleFileOutput_ ? &dependenceClosure : nullptr);
+ std::set<std::string> emittedModuleNames;
+ emittedModuleNames.insert(symbol.name().ToString());
auto asStr{GetAsString(symbol)};
+
+ // Emit additional modules for a hermetic module file
while (!additionalModules.empty()) {
UnorderedSymbolSet nextPass{std::move(additionalModules)};
additionalModules.clear();
for (const Symbol &modSym : nextPass) {
if (!modSym.owner().IsIntrinsicModules() &&
- hermeticModuleNames.find(modSym.name().ToString()) ==
- hermeticModuleNames.end()) {
- hermeticModuleNames.insert(modSym.name().ToString());
- PutSymbols(DEREF(modSym.scope()), &additionalModules);
+ emittedModuleNames.find(modSym.name().ToString()) ==
+ emittedModuleNames.end()) {
+ emittedModuleNames.insert(modSym.name().ToString());
+ PutSymbols(
+ DEREF(modSym.scope()), &additionalModules, &dependenceClosure);
asStr += GetAsString(modSym);
}
}
@@ -177,7 +187,8 @@ void ModFileWriter::WriteClosure(llvm::raw_ostream &out, const Symbol &symbol,
!nonIntrinsicModulesWritten.insert(symbol).second) {
return;
}
- PutSymbols(DEREF(symbol.scope()), /*hermeticModules=*/nullptr);
+ PutSymbols(DEREF(symbol.scope()), /*hermeticModules=*/nullptr,
+ /*dependenceClosure=*/nullptr);
needsBuf_.clear(); // omit module checksums
auto str{GetAsString(symbol)};
for (auto depRef : std::move(usedNonIntrinsicModules_)) {
@@ -223,78 +234,18 @@ std::string ModFileWriter::GetAsString(const Symbol &symbol) {
// Collect symbols from constant and specification expressions that are being
// referenced directly from other modules; they may require new USE
// associations.
-static void HarvestSymbolsNeededFromOtherModules(
- SourceOrderedSymbolSet &, const Scope &);
-static void HarvestSymbolsNeededFromOtherModules(
- SourceOrderedSymbolSet &set, const Symbol &symbol, const Scope &scope) {
- auto HarvestBound{[&](const Bound &bound) {
- if (const auto &expr{bound.GetExplicit()}) {
- for (SymbolRef ref : evaluate::CollectSymbols(*expr)) {
- set.emplace(*ref);
- }
- }
- }};
- auto HarvestShapeSpec{[&](const ShapeSpec &shapeSpec) {
- HarvestBound(shapeSpec.lbound());
- HarvestBound(shapeSpec.ubound());
- }};
- auto HarvestArraySpec{[&](const ArraySpec &arraySpec) {
- for (const auto &shapeSpec : arraySpec) {
- HarvestShapeSpec(shapeSpec);
- }
- }};
-
- if (symbol.has<DerivedTypeDetails>()) {
- if (symbol.scope()) {
- HarvestSymbolsNeededFromOtherModules(set, *symbol.scope());
- }
- } else if (const auto &generic{symbol.detailsIf<GenericDetails>()};
- generic && generic->derivedType()) {
- const Symbol &dtSym{*generic->derivedType()};
- if (dtSym.has<DerivedTypeDetails>()) {
- if (dtSym.scope()) {
- HarvestSymbolsNeededFromOtherModules(set, *dtSym.scope());
- }
- } else {
- CHECK(dtSym.has<UseDetails>() || dtSym.has<UseErrorDetails>());
- }
- } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
- HarvestArraySpec(object->shape());
- HarvestArraySpec(object->coshape());
- if (IsNamedConstant(symbol) || scope.IsDerivedType()) {
- if (object->init()) {
- for (SymbolRef ref : evaluate::CollectSymbols(*object->init())) {
- set.emplace(*ref);
- }
- }
- }
- } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
- if (proc->init() && *proc->init() && scope.IsDerivedType()) {
- set.emplace(**proc->init());
- }
- } else if (const auto *subp{symbol.detailsIf<SubprogramDetails>()}) {
- for (const Symbol *dummy : subp->dummyArgs()) {
- if (dummy) {
- HarvestSymbolsNeededFromOtherModules(set, *dummy, scope);
- }
- }
- if (subp->isFunction()) {
- HarvestSymbolsNeededFromOtherModules(set, subp->result(), scope);
- }
- }
-}
-
static void HarvestSymbolsNeededFromOtherModules(
SourceOrderedSymbolSet &set, const Scope &scope) {
- for (const auto &[_, symbol] : scope) {
- HarvestSymbolsNeededFromOtherModules(set, *symbol, scope);
+ for (const Symbol &symbol : CollectAllDependences(scope)) {
+ set.insert(symbol);
}
}
-void ModFileWriter::PrepareRenamings(const Scope &scope) {
+void ModFileWriter::PrepareRenamings(
+ const Scope &scope, const UnorderedSymbolSet *dependenceClosure) {
// Identify use-associated symbols already in scope under some name
std::map<const Symbol *, const Symbol *> useMap;
- for (const auto &[name, symbolRef] : scope) {
+ for (const auto &[_, symbolRef] : scope) {
const Symbol *symbol{&*symbolRef};
while (const auto *hostAssoc{symbol->detailsIf<HostAssocDetails>()}) {
symbol = &hostAssoc->symbol();
@@ -309,38 +260,42 @@ void ModFileWriter::PrepareRenamings(const Scope &scope) {
// Establish any necessary renamings of symbols in other modules
// to their names in this scope, creating those new names when needed.
auto &renamings{context_.moduleFileOutputRenamings()};
- for (SymbolRef s : symbolsNeeded) {
- if (s->owner().kind() != Scope::Kind::Module) {
+ for (const Symbol &sym : symbolsNeeded) {
+ if (sym.owner().kind() != Scope::Kind::Module) {
// Not a USE'able name from a module's top scope;
// component, binding, dummy argument, &c.
continue;
}
- const Scope *sMod{FindModuleContaining(s->owner())};
+ const Scope *sMod{FindModuleContaining(sym.owner())};
if (!sMod || sMod == &scope) {
continue;
}
- if (auto iter{useMap.find(&*s)}; iter != useMap.end()) {
- renamings.emplace(&*s, iter->second->name());
+ if (dependenceClosure &&
+ dependenceClosure->find(sym) == dependenceClosure->end()) {
continue;
}
- SourceName rename{s->name()};
- if (const Symbol * found{scope.FindSymbol(s->name())}) {
- if (found == &*s) {
+ if (auto iter{useMap.find(&sym)}; iter != useMap.end()) {
+ renamings.emplace(&sym, iter->second->name());
+ continue;
+ }
+ SourceName rename{sym.name()};
+ if (const Symbol *found{scope.FindSymbol(sym.name())}) {
+ if (found == &sym) {
continue; // available in scope
}
if (const auto *generic{found->detailsIf<GenericDetails>()}) {
- if (generic->derivedType() == &*s || generic->specific() == &*s) {
+ if (generic->derivedType() == &sym || generic->specific() == &sym) {
continue;
}
} else if (found->has<UseDetails>()) {
- if (&found->GetUltimate() == &*s) {
+ if (&found->GetUltimate() == &sym) {
continue; // already use-associated with same name
}
}
- if (&s->owner() != &found->owner()) { // Symbol needs renaming
+ if (&sym.owner() != &found->owner()) { // Symbol needs renaming
rename = scope.context().SaveTempName(
DEREF(sMod->symbol()).name().ToString() + "$" +
- s->name().ToString());
+ sym.name().ToString());
}
}
// Symbol is used in this scope but not visible under its name
@@ -350,26 +305,27 @@ void ModFileWriter::PrepareRenamings(const Scope &scope) {
uses_ << "use ";
}
uses_ << DEREF(sMod->symbol()).name() << ",only:";
- if (rename != s->name()) {
+ if (rename != sym.name()) {
uses_ << rename << "=>";
- renamings.emplace(&s->GetUltimate(), rename);
+ renamings.emplace(&sym.GetUltimate(), rename);
}
- uses_ << s->name() << '\n';
+ uses_ << sym.name() << '\n';
useExtraAttrs_ << "private::" << rename << '\n';
}
}
// Put out the visible symbols from scope.
-void ModFileWriter::PutSymbols(
- const Scope &scope, UnorderedSymbolSet *hermeticModules) {
+void ModFileWriter::PutSymbols(const Scope &scope,
+ UnorderedSymbolSet *hermeticModules,
+ UnorderedSymbolSet *dependenceClosure) {
SymbolVector sorted;
SymbolVector uses;
auto &renamings{context_.moduleFileOutputRenamings()};
auto previousRenamings{std::move(renamings)};
- PrepareRenamings(scope);
+ PrepareRenamings(scope, dependenceClosure);
SourceOrderedSymbolSet modules;
- CollectSymbols(scope, sorted, uses, modules);
- // Write module files for dependencies first so that their
+ CollectSymbols(scope, sorted, uses, modules, dependenceClosure);
+ // Write module files for dependences first so that their
// hashes are known.
for (const Symbol &mod : modules) {
if (hermeticModules) {
@@ -852,12 +808,17 @@ void ModFileWriter::PutUseExtraAttr(
// Collect the symbols of this scope sorted by their original order, not name.
// Generics and namelists are exceptions: they are sorted after other symbols.
void CollectSymbols(const Scope &scope, SymbolVector &sorted,
- SymbolVector &uses, SourceOrderedSymbolSet &modules) {
+ SymbolVector &uses, SourceOrderedSymbolSet &modules,
+ UnorderedSymbolSet *dependenceClosure) {
SymbolVector namelist, generics;
auto symbols{scope.GetSymbols()};
std::size_t commonSize{scope.commonBlocks().size()};
sorted.reserve(symbols.size() + commonSize);
for (const Symbol &symbol : symbols) {
+ if (dependenceClosure &&
+ dependenceClosure->find(symbol) == dependenceClosure->end()) {
+ continue; // needless for the main module
+ }
const auto *generic{symbol.detailsIf<GenericDetails>()};
if (generic) {
uses.insert(uses.end(), generic->uses().begin(), generic->uses().end());
diff --git a/flang/lib/Semantics/mod-file.h b/flang/lib/Semantics/mod-file.h
index 9e5724089b3c5..443596af19df3 100644
--- a/flang/lib/Semantics/mod-file.h
+++ b/flang/lib/Semantics/mod-file.h
@@ -67,8 +67,9 @@ class ModFileWriter {
void WriteOne(const Scope &);
void Write(const Symbol &);
std::string GetAsString(const Symbol &);
- void PrepareRenamings(const Scope &);
- void PutSymbols(const Scope &, UnorderedSymbolSet *hermetic);
+ void PrepareRenamings(const Scope &, const UnorderedSymbolSet *);
+ void PutSymbols(const Scope &, UnorderedSymbolSet *hermetic,
+ UnorderedSymbolSet *dependenceClosure);
// Returns true if a derived type with bindings and "contains" was emitted
bool PutComponents(const Symbol &);
void PutSymbol(llvm::raw_ostream &, const Symbol &);
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index f66918e5c140e..f6cbe49f56543 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -7416,7 +7416,8 @@ void DeclarationVisitor::SetType(
std::optional<DerivedTypeSpec> DeclarationVisitor::ResolveDerivedType(
const parser::Name &name) {
Scope &outer{NonDerivedTypeScope()};
- Symbol *symbol{FindSymbol(outer, name)};
+ Symbol *original{FindSymbol(outer, name)};
+ Symbol *symbol{original};
Symbol *ultimate{symbol ? &symbol->GetUltimate() : nullptr};
auto *generic{ultimate ? ultimate->detailsIf<GenericDetails>() : nullptr};
if (generic) {
@@ -7429,11 +7430,12 @@ std::optional<DerivedTypeSpec> DeclarationVisitor::ResolveDerivedType(
(generic && &ultimate->owner() == &outer)) {
if (allowForwardReferenceToDerivedType()) {
if (!symbol) {
- symbol = &MakeSymbol(outer, name.source, Attrs{});
+ symbol = original = &MakeSymbol(outer, name.source, Attrs{});
Resolve(name, *symbol);
} else if (generic) {
// forward ref to type with later homonymous generic
- symbol = &outer.MakeSymbol(name.source, Attrs{}, UnknownDetails{});
+ symbol = original =
+ &outer.MakeSymbol(name.source, Attrs{}, UnknownDetails{});
generic->set_derivedType(*symbol);
name.symbol = symbol;
}
@@ -7453,7 +7455,7 @@ std::optional<DerivedTypeSpec> DeclarationVisitor::ResolveDerivedType(
if (CheckUseError(name)) {
return std::nullopt;
} else if (symbol->GetUltimate().has<DerivedTypeDetails>()) {
- return DerivedTypeSpec{name.source, *symbol};
+ return DerivedTypeSpec{name.source, *original};
} else {
Say(name, "'%s' is not a derived type"_err_en_US);
return std::nullopt;
diff --git a/flang/lib/Semantics/symbol-set-closure.cpp b/flang/lib/Semantics/symbol-set-closure.cpp
new file mode 100644
index 0000000000000..fb928460b86b5
--- /dev/null
+++ b/flang/lib/Semantics/symbol-set-closure.cpp
@@ -0,0 +1,187 @@
+//===-- lib/Semantics/symbol-set-closure.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Semantics/symbol-set-closure.h"
+#include "flang/Common/idioms.h"
+#include "flang/Common/visit.h"
+
+namespace Fortran::semantics {
+
+class Collector {
+public:
+ explicit Collector(int flags) : flags_{flags} {}
+
+ UnorderedSymbolSet Collected() { return std::move(set_); }
+
+ void operator()(const Symbol &x) { set_.insert(x); }
+ void operator()(SymbolRef x) { (*this)(*x); }
+ template <typename A> void operator()(const std::optional<A> &x) {
+ if (x) {
+ (*this)(*x);
+ }
+ }
+ template <typename A> void operator()(const A *x) {
+ if (x) {
+ (*this)(*x);
+ }
+ }
+ void operator()(const UnorderedSymbolSet &x) {
+ for (const Symbol &symbol : x) {
+ (*this)(symbol);
+ }
+ }
+ void operator()(const SourceOrderedSymbolSet &x) {
+ for (const Symbol &symbol : x) {
+ (*this)(symbol);
+ }
+ }
+ void operator()(const Scope &x) {
+ for (const auto &[_, ref] : x) {
+ (*this)(*ref);
+ }
+ }
+ template <typename T> void operator()(const evaluate::Expr<T> &x) {
+ UnorderedSymbolSet exprSyms{evaluate::CollectSymbols(x)};
+ for (const Symbol &sym : exprSyms) {
+ if (!sym.owner().IsDerivedType() || sym.has<DerivedTypeDetails>() ||
+ (flags_ & IncludeComponentsInExprs)) {
+ (*this)(sym);
+ }
+ }
+ }
+ void operator()(const DeclTypeSpec &type) {
+ if (type.category() == DeclTypeSpec::Category::Character) {
+ (*this)(type.characterTypeSpec().length());
+ } else {
+ (*this)(type.AsDerived());
+ }
+ }
+ void operator()(const DerivedTypeSpec &type) {
+ (*this)(type.originalTypeSymbol());
+ for (const auto &[_, value] : type.parameters()) {
+ (*this)(value);
+ }
+ }
+ void operator()(const ParamValue &x) { (*this)(x.GetExplicit()); }
+ void operator()(const Bound &x) { (*this)(x.GetExplicit()); }
+ void operator()(const ShapeSpec &x) {
+ (*this)(x.lbound());
+ (*this)(x.ubound());
+ }
+ void operator()(const ArraySpec &x) {
+ for (const ShapeSpec &shapeSpec : x) {
+ (*this)(shapeSpec);
+ }
+ }
+
+private:
+ UnorderedSymbolSet set_;
+ int flags_{NoDependenceCollectionFlags};
+};
+
+UnorderedSymbolSet CollectAllDependences(const Scope &scope, int flags) {
+ UnorderedSymbolSet basis;
+ for (const auto &[_, symbol] : scope) {
+ basis.insert(*symbol);
+ }
+ return CollectAllDependences(basis, flags);
+}
+
+UnorderedSymbolSet CollectAllDependences(
+ const UnorderedSymbolSet &original, int flags) {
+ UnorderedSymbolSet result;
+ if (flags & IncludeOriginalSymbols) {
+ result = original;
+ }
+ UnorderedSymbolSet work{original};
+ while (!work.empty()) {
+ Collector collect{flags};
+ for (const Symbol &symbol : work) {
+ if (symbol.test(Symbol::Flag::CompilerCreated)) {
+ continue;
+ }
+ collect(symbol.GetType());
+ common::visit(
+ common::visitors{
+ [&collect, &symbol](const ObjectEntityDetails &x) {
+ collect(x.shape());
+ collect(x.coshape());
+ if (IsNamedConstant(symbol) || symbol.owner().IsDerivedType()) {
+ collect(x.init());
+ }
+ collect(x.commonBlock());
+ if (const auto *set{FindEquivalenceSet(symbol)}) {
+...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/144765
More information about the flang-commits
mailing list