Skip to content

Commit 6598795

Browse files
authored
[flang] Add -fhermetic-module-files (#98083)
Module files emitted by this Fortran compiler are valid Fortran source files. Symbols that are USE-associated into modules are represented in their module files with USE statements and special comments with hash codes in them to ensure that those USE statements resolve to the same modules that were used to build the module when its module file was generated. This scheme prevents unchecked module file growth in large applications by not emitting USE-associated symbols redundantly. This problem can be especially bad when derived type definitions must be repeated in the module files of their clients, and the clients of those modules, and so on. However, this scheme has the disadvantage that clients of modules must be compiled with dependent modules in the module search path. This new -fhermetic-module-files option causes module file output to be free of dependences on any non-intrinsic module files; dependent modules are instead emitted as part of the module file, rather than being USE-associated. It is intended for top level library module files that are shipped with binary libraries when it is not convenient to collect and ship their dependent module files as well. Fixes #97398.
1 parent c9a4a27 commit 6598795

File tree

10 files changed

+142
-30
lines changed

10 files changed

+142
-30
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6687,6 +6687,9 @@ defm stack_arrays : BoolOptionWithoutMarshalling<"f", "stack-arrays",
66876687
defm loop_versioning : BoolOptionWithoutMarshalling<"f", "version-loops-for-stride",
66886688
PosFlag<SetTrue, [], [ClangOption], "Create unit-strided versions of loops">,
66896689
NegFlag<SetFalse, [], [ClangOption], "Do not create unit-strided loops (default)">>;
6690+
6691+
def fhermetic_module_files : Flag<["-"], "fhermetic-module-files">, Group<f_Group>,
6692+
HelpText<"Emit hermetic module files (no nested USE association)">;
66906693
} // let Visibility = [FC1Option, FlangOption]
66916694

66926695
def J : JoinedOrSeparate<["-"], "J">,

clang/lib/Driver/ToolChains/Flang.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,26 @@ static void addDashXForInput(const ArgList &Args, const InputInfo &Input,
3535

3636
void Flang::addFortranDialectOptions(const ArgList &Args,
3737
ArgStringList &CmdArgs) const {
38-
Args.addAllArgs(
39-
CmdArgs, {options::OPT_ffixed_form, options::OPT_ffree_form,
40-
options::OPT_ffixed_line_length_EQ, options::OPT_fopenacc,
41-
options::OPT_finput_charset_EQ, options::OPT_fimplicit_none,
42-
options::OPT_fno_implicit_none, options::OPT_fbackslash,
43-
options::OPT_fno_backslash, options::OPT_flogical_abbreviations,
44-
options::OPT_fno_logical_abbreviations,
45-
options::OPT_fxor_operator, options::OPT_fno_xor_operator,
46-
options::OPT_falternative_parameter_statement,
47-
options::OPT_fdefault_real_8, options::OPT_fdefault_integer_8,
48-
options::OPT_fdefault_double_8, options::OPT_flarge_sizes,
49-
options::OPT_fno_automatic});
38+
Args.addAllArgs(CmdArgs, {options::OPT_ffixed_form,
39+
options::OPT_ffree_form,
40+
options::OPT_ffixed_line_length_EQ,
41+
options::OPT_fopenacc,
42+
options::OPT_finput_charset_EQ,
43+
options::OPT_fimplicit_none,
44+
options::OPT_fno_implicit_none,
45+
options::OPT_fbackslash,
46+
options::OPT_fno_backslash,
47+
options::OPT_flogical_abbreviations,
48+
options::OPT_fno_logical_abbreviations,
49+
options::OPT_fxor_operator,
50+
options::OPT_fno_xor_operator,
51+
options::OPT_falternative_parameter_statement,
52+
options::OPT_fdefault_real_8,
53+
options::OPT_fdefault_integer_8,
54+
options::OPT_fdefault_double_8,
55+
options::OPT_flarge_sizes,
56+
options::OPT_fno_automatic,
57+
options::OPT_fhermetic_module_files});
5058
}
5159

5260
void Flang::addPreprocessingOptions(const ArgList &Args,

flang/include/flang/Frontend/CompilerInvocation.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ class CompilerInvocation : public CompilerInvocationBase {
9999
std::string moduleFileSuffix = ".mod";
100100

101101
bool debugModuleDir = false;
102+
bool hermeticModuleFileOutput = false;
102103

103104
bool warnAsErr = false;
104105

@@ -179,6 +180,11 @@ class CompilerInvocation : public CompilerInvocationBase {
179180
bool &getDebugModuleDir() { return debugModuleDir; }
180181
const bool &getDebugModuleDir() const { return debugModuleDir; }
181182

183+
bool &getHermeticModuleFileOutput() { return hermeticModuleFileOutput; }
184+
const bool &getHermeticModuleFileOutput() const {
185+
return hermeticModuleFileOutput;
186+
}
187+
182188
bool &getWarnAsErr() { return warnAsErr; }
183189
const bool &getWarnAsErr() const { return warnAsErr; }
184190

@@ -244,6 +250,9 @@ class CompilerInvocation : public CompilerInvocationBase {
244250
}
245251

246252
void setDebugModuleDir(bool flag) { debugModuleDir = flag; }
253+
void setHermeticModuleFileOutput(bool flag) {
254+
hermeticModuleFileOutput = flag;
255+
}
247256

248257
void setWarnAsErr(bool flag) { warnAsErr = flag; }
249258

flang/include/flang/Semantics/semantics.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,11 @@ class SemanticsContext {
307307

308308
class Semantics {
309309
public:
310-
explicit Semantics(SemanticsContext &context, parser::Program &program,
311-
bool debugModuleWriter = false)
312-
: context_{context}, program_{program} {
313-
context.set_debugModuleWriter(debugModuleWriter);
310+
explicit Semantics(SemanticsContext &context, parser::Program &program)
311+
: context_{context}, program_{program} {}
312+
Semantics &set_hermeticModuleFileOutput(bool yes = true) {
313+
hermeticModuleFileOutput_ = yes;
314+
return *this;
314315
}
315316

316317
SemanticsContext &context() const { return context_; }
@@ -326,6 +327,7 @@ class Semantics {
326327
private:
327328
SemanticsContext &context_;
328329
parser::Program &program_;
330+
bool hermeticModuleFileOutput_{false};
329331
};
330332

331333
// Base class for semantics checkers.

flang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,11 @@ static bool parseSemaArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
833833
res.setDebugModuleDir(true);
834834
}
835835

836+
// -fhermetic-module-files option
837+
if (args.hasArg(clang::driver::options::OPT_fhermetic_module_files)) {
838+
res.setHermeticModuleFileOutput(true);
839+
}
840+
836841
// -module-suffix
837842
if (const auto *moduleSuffix =
838843
args.getLastArg(clang::driver::options::OPT_module_suffix)) {

flang/lib/Frontend/FrontendAction.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,14 @@ bool FrontendAction::runSemanticChecks() {
180180
// so that they are merged and all printed in order.
181181
auto &semanticsCtx{ci.getSemanticsContext()};
182182
semanticsCtx.messages().Annex(std::move(ci.getParsing().messages()));
183+
semanticsCtx.set_debugModuleWriter(ci.getInvocation().getDebugModuleDir());
183184

184185
// Prepare semantics
185-
ci.setSemantics(std::make_unique<Fortran::semantics::Semantics>(
186-
semanticsCtx, *parseTree, ci.getInvocation().getDebugModuleDir()));
186+
ci.setSemantics(std::make_unique<Fortran::semantics::Semantics>(semanticsCtx,
187+
*parseTree));
187188
auto &semantics = ci.getSemantics();
189+
semantics.set_hermeticModuleFileOutput(
190+
ci.getInvocation().getHermeticModuleFileOutput());
188191

189192
// Run semantic checks
190193
semantics.Perform();

flang/lib/Semantics/mod-file.cpp

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,26 @@ void ModFileWriter::Write(const Symbol &symbol) {
141141
auto ancestorName{ancestor ? ancestor->GetName().value().ToString() : ""s};
142142
auto path{context_.moduleDirectory() + '/' +
143143
ModFileName(symbol.name(), ancestorName, context_.moduleFileSuffix())};
144-
PutSymbols(DEREF(symbol.scope()));
144+
145+
UnorderedSymbolSet hermeticModules;
146+
hermeticModules.insert(symbol);
147+
UnorderedSymbolSet additionalModules;
148+
PutSymbols(DEREF(symbol.scope()),
149+
hermeticModuleFileOutput_ ? &additionalModules : nullptr);
150+
auto asStr{GetAsString(symbol)};
151+
while (!additionalModules.empty()) {
152+
for (auto ref : UnorderedSymbolSet{std::move(additionalModules)}) {
153+
if (hermeticModules.insert(*ref).second &&
154+
!ref->owner().IsIntrinsicModules()) {
155+
PutSymbols(DEREF(ref->scope()), &additionalModules);
156+
asStr += GetAsString(*ref);
157+
}
158+
}
159+
}
160+
145161
ModuleCheckSumType checkSum;
146-
if (std::error_code error{WriteFile(
147-
path, GetAsString(symbol), checkSum, context_.debugModuleWriter())}) {
162+
if (std::error_code error{
163+
WriteFile(path, asStr, checkSum, context_.debugModuleWriter())}) {
148164
context_.Say(
149165
symbol.name(), "Error writing %s: %s"_err_en_US, path, error.message());
150166
}
@@ -157,7 +173,7 @@ void ModFileWriter::WriteClosure(llvm::raw_ostream &out, const Symbol &symbol,
157173
!nonIntrinsicModulesWritten.insert(symbol).second) {
158174
return;
159175
}
160-
PutSymbols(DEREF(symbol.scope()));
176+
PutSymbols(DEREF(symbol.scope()), /*hermeticModules=*/nullptr);
161177
needsBuf_.clear(); // omit module checksums
162178
auto str{GetAsString(symbol)};
163179
for (auto depRef : std::move(usedNonIntrinsicModules_)) {
@@ -338,7 +354,8 @@ void ModFileWriter::PrepareRenamings(const Scope &scope) {
338354
}
339355

340356
// Put out the visible symbols from scope.
341-
void ModFileWriter::PutSymbols(const Scope &scope) {
357+
void ModFileWriter::PutSymbols(
358+
const Scope &scope, UnorderedSymbolSet *hermeticModules) {
342359
SymbolVector sorted;
343360
SymbolVector uses;
344361
auto &renamings{context_.moduleFileOutputRenamings()};
@@ -349,11 +366,16 @@ void ModFileWriter::PutSymbols(const Scope &scope) {
349366
// Write module files for dependencies first so that their
350367
// hashes are known.
351368
for (auto ref : modules) {
352-
Write(*ref);
353-
needs_ << ModHeader::need
354-
<< CheckSumString(ref->get<ModuleDetails>().moduleFileHash().value())
355-
<< (ref->owner().IsIntrinsicModules() ? " i " : " n ")
356-
<< ref->name().ToString() << '\n';
369+
if (hermeticModules) {
370+
hermeticModules->insert(*ref);
371+
} else {
372+
Write(*ref);
373+
needs_ << ModHeader::need
374+
<< CheckSumString(
375+
ref->get<ModuleDetails>().moduleFileHash().value())
376+
<< (ref->owner().IsIntrinsicModules() ? " i " : " n ")
377+
<< ref->name().ToString() << '\n';
378+
}
357379
}
358380
std::string buf; // stuff after CONTAINS in derived type
359381
llvm::raw_string_ostream typeBindings{buf};

flang/lib/Semantics/mod-file.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ class ModFileWriter {
3737
bool WriteAll();
3838
void WriteClosure(llvm::raw_ostream &, const Symbol &,
3939
UnorderedSymbolSet &nonIntrinsicModulesWritten);
40+
ModFileWriter &set_hermeticModuleFileOutput(bool yes = true) {
41+
hermeticModuleFileOutput_ = yes;
42+
return *this;
43+
}
4044

4145
private:
4246
SemanticsContext &context_;
@@ -57,13 +61,14 @@ class ModFileWriter {
5761
llvm::raw_string_ostream decls_{declsBuf_};
5862
llvm::raw_string_ostream contains_{containsBuf_};
5963
bool isSubmodule_{false};
64+
bool hermeticModuleFileOutput_{false};
6065

6166
void WriteAll(const Scope &);
6267
void WriteOne(const Scope &);
6368
void Write(const Symbol &);
6469
std::string GetAsString(const Symbol &);
6570
void PrepareRenamings(const Scope &);
66-
void PutSymbols(const Scope &);
71+
void PutSymbols(const Scope &, UnorderedSymbolSet *hermetic);
6772
// Returns true if a derived type with bindings and "contains" was emitted
6873
bool PutComponents(const Symbol &);
6974
void PutSymbol(llvm::raw_ostream &, const Symbol &);

flang/lib/Semantics/semantics.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,9 @@ bool Semantics::Perform() {
602602
CanonicalizeCUDA(program_) &&
603603
CanonicalizeDirectives(context_.messages(), program_) &&
604604
PerformStatementSemantics(context_, program_) &&
605-
ModFileWriter{context_}.WriteAll();
605+
ModFileWriter{context_}
606+
.set_hermeticModuleFileOutput(hermeticModuleFileOutput_)
607+
.WriteAll();
606608
}
607609

608610
void Semantics::EmitMessages(llvm::raw_ostream &os) {

flang/test/Semantics/modfile65.f90

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
! RUN: %python %S/test_modfile.py %s %flang_fc1 -fhermetic-module-files
2+
module m1
3+
integer, parameter :: n = 123
4+
end
5+
6+
module m2
7+
use m1
8+
end
9+
10+
module m3
11+
use m1, m => n
12+
end
13+
14+
module m4
15+
use m2
16+
use m3
17+
end
18+
19+
!Expect: m1.mod
20+
!module m1
21+
!integer(4),parameter::n=123_4
22+
!end
23+
24+
!Expect: m2.mod
25+
!module m2
26+
!use m1,only:n
27+
!end
28+
!module m1
29+
!integer(4),parameter::n=123_4
30+
!end
31+
32+
!Expect: m3.mod
33+
!module m3
34+
!use m1,only:m=>n
35+
!end
36+
!module m1
37+
!integer(4),parameter::n=123_4
38+
!end
39+
40+
!Expect: m4.mod
41+
!module m4
42+
!use m2,only:n
43+
!use m3,only:m
44+
!end
45+
!module m2
46+
!use m1,only:n
47+
!end
48+
!module m3
49+
!use m1,only:m=>n
50+
!end
51+
!module m1
52+
!integer(4),parameter::n=123_4
53+
!end

0 commit comments

Comments
 (0)