Skip to content

Commit f7a15e0

Browse files
authored
[flang] Use module file hashes for more checking and disambiguation (#80354)
f18's module files are Fortran with a leading header comment containing the module file format version and a hash of the following contents. This hash is currently used only to protect module files against corruption and truncation. Extend the use of these hashes to catch or avoid some error cases. When one module file depends upon another, note its hash in additional module file header comments. This allows the compiler to detect when the module dependency is on a module file that has been updated. Further, it allows the compiler to find the right module file dependency when the same module file name appears in multiple directories on the module search path. The order in which module files are written, when multiple modules appear in a source file, is such that every dependency is written before the module(s) that depend upon it, so that their hashes are known. A warning is emitted when a module file is not the first hit on the module file search path. Further work is needed to add a compiler option that emits (larger) stand-alone module files that incorporate copies of their dependencies rather than relying on search paths. This will be desirable for application libraries that want to ship only "top-level" module files without needing to include their dependencies. Another future work item would be to admit multiple modules in the same compilation with the same name if they have distinct hashes.
1 parent 2fb764d commit f7a15e0

19 files changed

+334
-46
lines changed

flang/include/flang/Parser/provenance.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ class AllSources {
151151

152152
void ClearSearchPath();
153153
void AppendSearchPathDirectory(std::string); // new last directory
154+
const SourceFile *OpenPath(std::string path, llvm::raw_ostream &error);
154155
const SourceFile *Open(std::string path, llvm::raw_ostream &error,
155156
std::optional<std::string> &&prependPath = std::nullopt);
156157
const SourceFile *ReadStandardInput(llvm::raw_ostream &error);

flang/include/flang/Parser/source.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ namespace Fortran::parser {
3636
std::string DirectoryName(std::string path);
3737
std::optional<std::string> LocateSourceFile(
3838
std::string name, const std::list<std::string> &searchPath);
39+
std::vector<std::string> LocateSourceFileAll(
40+
std::string name, const std::vector<std::string> &searchPath);
3941

4042
class SourceFile;
4143

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===-- include/flang/Semantics/module-dependences.h ------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef FORTRAN_SEMANTICS_MODULE_DEPENDENCES_H_
10+
#define FORTRAN_SEMANTICS_MODULE_DEPENDENCES_H_
11+
12+
#include <cinttypes>
13+
#include <map>
14+
#include <optional>
15+
#include <string>
16+
17+
namespace Fortran::semantics {
18+
19+
using ModuleCheckSumType = std::uint64_t;
20+
21+
class ModuleDependences {
22+
public:
23+
void AddDependence(
24+
std::string &&name, bool intrinsic, ModuleCheckSumType hash) {
25+
if (intrinsic) {
26+
intrinsicMap_.emplace(std::move(name), hash);
27+
} else {
28+
nonIntrinsicMap_.emplace(std::move(name), hash);
29+
}
30+
}
31+
std::optional<ModuleCheckSumType> GetRequiredHash(
32+
const std::string &name, bool intrinsic) {
33+
if (intrinsic) {
34+
if (auto iter{intrinsicMap_.find(name)}; iter != intrinsicMap_.end()) {
35+
return iter->second;
36+
}
37+
} else {
38+
if (auto iter{nonIntrinsicMap_.find(name)};
39+
iter != nonIntrinsicMap_.end()) {
40+
return iter->second;
41+
}
42+
}
43+
return std::nullopt;
44+
}
45+
46+
private:
47+
std::map<std::string, ModuleCheckSumType> intrinsicMap_, nonIntrinsicMap_;
48+
};
49+
50+
} // namespace Fortran::semantics
51+
#endif // FORTRAN_SEMANTICS_MODULE_DEPENDENCES_H_

flang/include/flang/Semantics/semantics.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "flang/Evaluate/intrinsics.h"
1717
#include "flang/Evaluate/target.h"
1818
#include "flang/Parser/message.h"
19+
#include "flang/Semantics/module-dependences.h"
1920
#include <iosfwd>
2021
#include <set>
2122
#include <string>
@@ -108,6 +109,7 @@ class SemanticsContext {
108109
parser::Messages &messages() { return messages_; }
109110
evaluate::FoldingContext &foldingContext() { return foldingContext_; }
110111
parser::AllCookedSources &allCookedSources() { return allCookedSources_; }
112+
ModuleDependences &moduleDependences() { return moduleDependences_; }
111113

112114
SemanticsContext &set_location(
113115
const std::optional<parser::CharBlock> &location) {
@@ -293,6 +295,7 @@ class SemanticsContext {
293295
const Scope *ppcBuiltinsScope_{nullptr}; // module __ppc_intrinsics
294296
std::list<parser::Program> modFileParseTrees_;
295297
std::unique_ptr<CommonBlockMap> commonBlockMap_;
298+
ModuleDependences moduleDependences_;
296299
};
297300

298301
class Semantics {

flang/include/flang/Semantics/symbol.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "flang/Common/enum-set.h"
1515
#include "flang/Common/reference.h"
1616
#include "flang/Common/visit.h"
17+
#include "flang/Semantics/module-dependences.h"
1718
#include "llvm/ADT/DenseMapInfo.h"
1819

1920
#include <array>
@@ -86,11 +87,16 @@ class ModuleDetails : public WithOmpDeclarative {
8687
void set_scope(const Scope *);
8788
bool isDefaultPrivate() const { return isDefaultPrivate_; }
8889
void set_isDefaultPrivate(bool yes = true) { isDefaultPrivate_ = yes; }
90+
std::optional<ModuleCheckSumType> moduleFileHash() const {
91+
return moduleFileHash_;
92+
}
93+
void set_moduleFileHash(ModuleCheckSumType x) { moduleFileHash_ = x; }
8994

9095
private:
9196
bool isSubmodule_;
9297
bool isDefaultPrivate_{false};
9398
const Scope *scope_{nullptr};
99+
std::optional<ModuleCheckSumType> moduleFileHash_;
94100
};
95101

96102
class MainProgramDetails : public WithOmpDeclarative {
@@ -1035,7 +1041,7 @@ struct SymbolAddressCompare {
10351041
// Symbol comparison is usually based on the order of cooked source
10361042
// stream creation and, when both are from the same cooked source,
10371043
// their positions in that cooked source stream.
1038-
// Don't use this comparator or OrderedSymbolSet to hold
1044+
// Don't use this comparator or SourceOrderedSymbolSet to hold
10391045
// Symbols that might be subject to ReplaceName().
10401046
struct SymbolSourcePositionCompare {
10411047
// These functions are implemented in Evaluate/tools.cpp to

flang/lib/Parser/provenance.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@ void AllSources::AppendSearchPathDirectory(std::string directory) {
167167
searchPath_.push_back(directory);
168168
}
169169

170+
const SourceFile *AllSources::OpenPath(
171+
std::string path, llvm::raw_ostream &error) {
172+
std::unique_ptr<SourceFile> source{std::make_unique<SourceFile>(encoding_)};
173+
if (source->Open(path, error)) {
174+
return ownedSourceFiles_.emplace_back(std::move(source)).get();
175+
} else {
176+
return nullptr;
177+
}
178+
}
179+
170180
const SourceFile *AllSources::Open(std::string path, llvm::raw_ostream &error,
171181
std::optional<std::string> &&prependPath) {
172182
std::unique_ptr<SourceFile> source{std::make_unique<SourceFile>(encoding_)};
@@ -180,12 +190,10 @@ const SourceFile *AllSources::Open(std::string path, llvm::raw_ostream &error,
180190
if (prependPath) {
181191
searchPath_.pop_front();
182192
}
183-
if (!found) {
184-
error << "Source file '" << path << "' was not found";
185-
return nullptr;
186-
} else if (source->Open(*found, error)) {
187-
return ownedSourceFiles_.emplace_back(std::move(source)).get();
193+
if (found) {
194+
return OpenPath(*found, error);
188195
} else {
196+
error << "Source file '" << path << "' was not found";
189197
return nullptr;
190198
}
191199
}

flang/lib/Parser/source.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,24 @@ std::optional<std::string> LocateSourceFile(
7575
return std::nullopt;
7676
}
7777

78+
std::vector<std::string> LocateSourceFileAll(
79+
std::string name, const std::vector<std::string> &searchPath) {
80+
if (name == "-" || llvm::sys::path::is_absolute(name)) {
81+
return {name};
82+
}
83+
std::vector<std::string> result;
84+
for (const std::string &dir : searchPath) {
85+
llvm::SmallString<128> path{dir};
86+
llvm::sys::path::append(path, name);
87+
bool isDir{false};
88+
auto er = llvm::sys::fs::is_directory(path, isDir);
89+
if (!er && !isDir) {
90+
result.emplace_back(path.str().str());
91+
}
92+
}
93+
return result;
94+
}
95+
7896
std::size_t RemoveCarriageReturns(llvm::MutableArrayRef<char> buf) {
7997
std::size_t wrote{0};
8098
char *buffer{buf.data()};

0 commit comments

Comments
 (0)