Skip to content

Commit 040e126

Browse files
committed
Support lazy stat'ing of files referenced by module maps.
This patch adds support for a `header` declaration in a module map to specify certain `stat` information (currently, size and mtime) about that header file. This has two purposes: - It removes the need to eagerly `stat` every file referenced by a module map. Instead, we track a list of unresolved header files with each size / mtime (actually, for simplicity, we track submodules with such headers), and when attempting to look up a header file based on a `FileEntry`, we check if there are any unresolved header directives with that `FileEntry`'s size / mtime and perform deferred `stat`s if so. - It permits a preprocessed module to be compiled without the original files being present on disk. The only reason we used to need those files was to get the `stat` information in order to do header -> module lookups when using the module. If we're provided with the `stat` information in the preprocessed module, we can avoid requiring the files to exist. Unlike most `header` directives, if a `header` directive with `stat` information has no corresponding on-disk file the enclosing module is *not* marked unavailable (so that behavior is consistent regardless of whether we've resolved a header directive, and so that preprocessed modules don't get marked unavailable). We could actually do this for all `header` directives: the only reason we mark the module unavailable if headers are missing is to give a diagnostic slightly earlier (rather than waiting until we actually try to build the module / load and validate its .pcm file). Differential Revision: https://reviews.llvm.org/D33703 llvm-svn: 304515
1 parent ae80045 commit 040e126

20 files changed

+546
-152
lines changed

clang/docs/Modules.rst

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -469,9 +469,16 @@ A header declaration specifies that a particular header is associated with the e
469469
.. parsed-literal::
470470
471471
*header-declaration*:
472-
``private``:sub:`opt` ``textual``:sub:`opt` ``header`` *string-literal*
473-
``umbrella`` ``header`` *string-literal*
474-
``exclude`` ``header`` *string-literal*
472+
``private``:sub:`opt` ``textual``:sub:`opt` ``header`` *string-literal* *header-attrs*:sub:`opt`
473+
``umbrella`` ``header`` *string-literal* *header-attrs*:sub:`opt`
474+
``exclude`` ``header`` *string-literal* *header-attrs*:sub:`opt`
475+
476+
*header-attrs*:
477+
'{' *header-attr** '}'
478+
479+
*header-attr*:
480+
``size`` *integer-literal*
481+
``mtime`` *integer-literal*
475482
476483
A header declaration that does not contain ``exclude`` nor ``textual`` specifies a header that contributes to the enclosing module. Specifically, when the module is built, the named header will be parsed and its declarations will be (logically) placed into the enclosing submodule.
477484

@@ -504,6 +511,18 @@ A header with the ``exclude`` specifier is excluded from the module. It will not
504511
505512
A given header shall not be referenced by more than one *header-declaration*.
506513

514+
Two *header-declaration*\s, or a *header-declaration* and a ``#include``, are
515+
considered to refer to the same file if the paths resolve to the same file
516+
and the specified *header-attr*\s (if any) match the attributes of that file,
517+
even if the file is named differently (for instance, by a relative path or
518+
via symlinks).
519+
520+
.. note::
521+
The use of *header-attr*\s avoids the need for Clang to speculatively
522+
``stat`` every header referenced by a module map. It is recommended that
523+
*header-attr*\s only be used in machine-generated module maps, to avoid
524+
mismatches between attribute values and the corresponding files.
525+
507526
Umbrella directory declaration
508527
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
509528
An umbrella directory declaration specifies that all of the headers in the specified directory should be included within the module.

clang/include/clang/Basic/DiagnosticLexKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,12 @@ def warn_mmap_mismatched_top_level_private : Warning<
664664
InGroup<PrivateModule>;
665665
def note_mmap_rename_top_level_private_as_submodule : Note<
666666
"make '%0' a submodule of '%1' to ensure it can be found by name">;
667+
def err_mmap_duplicate_header_attribute : Error<
668+
"header attribute '%0' specified multiple times">;
669+
def err_mmap_invalid_header_attribute_value : Error<
670+
"expected integer literal as value for header attribute '%0'">;
671+
def err_mmap_expected_header_attribute : Error<
672+
"expected a header attribute name ('size' or 'mtime')">;
667673

668674
def warn_auto_module_import : Warning<
669675
"treating #%select{include|import|include_next|__include_macros}0 as an "

clang/include/clang/Basic/DiagnosticSerializationKinds.td

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,6 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
174174
"method %2 with %ordinal3 parameter of type %4%select{| decayed from %6}5|"
175175
"method %2 with %ordinal3 parameter named %4}1">;
176176

177-
def warn_module_uses_date_time : Warning<
178-
"%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
179-
InGroup<DiagGroup<"pch-date-time">>;
180-
181177
def warn_duplicate_module_file_extension : Warning<
182178
"duplicate module file extension block name '%0'">,
183179
InGroup<ModuleFileExtension>;
@@ -186,7 +182,15 @@ def warn_module_system_bit_conflict : Warning<
186182
"module file '%0' was validated as a system module and is now being imported "
187183
"as a non-system module; any difference in diagnostic options will be ignored">,
188184
InGroup<ModuleConflict>;
185+
} // let CategoryName
189186

187+
let CategoryName = "AST Serialization Issue" in {
188+
def warn_module_uses_date_time : Warning<
189+
"%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
190+
InGroup<DiagGroup<"pch-date-time">>;
191+
def err_module_no_size_mtime_for_header : Error<
192+
"cannot emit module %0: %select{size|mtime}1 must be explicitly specified "
193+
"for missing header file \"%2\"">;
190194
} // let CategoryName
191195
} // let Component
192196

clang/include/clang/Basic/Module.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,19 @@ class Module {
154154
/// \brief Stored information about a header directive that was found in the
155155
/// module map file but has not been resolved to a file.
156156
struct UnresolvedHeaderDirective {
157+
HeaderKind Kind = HK_Normal;
157158
SourceLocation FileNameLoc;
158159
std::string FileName;
159-
bool IsUmbrella;
160+
bool IsUmbrella = false;
161+
bool HasBuiltinHeader = false;
162+
Optional<off_t> Size;
163+
Optional<time_t> ModTime;
160164
};
161165

166+
/// Headers that are mentioned in the module map file but that we have not
167+
/// yet attempted to resolve to a file on the file system.
168+
SmallVector<UnresolvedHeaderDirective, 1> UnresolvedHeaders;
169+
162170
/// \brief Headers that are mentioned in the module map file but could not be
163171
/// found on the file system.
164172
SmallVector<UnresolvedHeaderDirective, 1> MissingHeaders;

clang/include/clang/Lex/ModuleMap.h

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/ADT/SmallVector.h"
2727
#include "llvm/ADT/StringMap.h"
2828
#include "llvm/ADT/StringRef.h"
29+
#include "llvm/ADT/TinyPtrVector.h"
2930
#include "llvm/ADT/Twine.h"
3031
#include <algorithm>
3132
#include <memory>
@@ -116,6 +117,11 @@ class ModuleMap {
116117
// Adjust ModuleMap::addHeader.
117118
};
118119

120+
/// Convert a header kind to a role. Requires Kind to not be HK_Excluded.
121+
static ModuleHeaderRole headerKindToRole(Module::HeaderKind Kind);
122+
/// Convert a header role to a kind.
123+
static Module::HeaderKind headerRoleToKind(ModuleHeaderRole Role);
124+
119125
/// \brief A header that is known to reside within a given module,
120126
/// whether it was included or excluded.
121127
class KnownHeader {
@@ -165,7 +171,13 @@ class ModuleMap {
165171
/// \brief Mapping from each header to the module that owns the contents of
166172
/// that header.
167173
HeadersMap Headers;
168-
174+
175+
/// Map from file sizes to modules with lazy header directives of that size.
176+
mutable llvm::DenseMap<off_t, llvm::TinyPtrVector<Module*>> LazyHeadersBySize;
177+
/// Map from mtimes to modules with lazy header directives with those mtimes.
178+
mutable llvm::DenseMap<time_t, llvm::TinyPtrVector<Module*>>
179+
LazyHeadersByModTime;
180+
169181
/// \brief Mapping from directories with umbrella headers to the module
170182
/// that is generated from the umbrella header.
171183
///
@@ -257,22 +269,30 @@ class ModuleMap {
257269
/// resolved.
258270
Module *resolveModuleId(const ModuleId &Id, Module *Mod, bool Complain) const;
259271

260-
/// Resolve the given header directive to an actual header file.
272+
/// Add an unresolved header to a module.
273+
void addUnresolvedHeader(Module *Mod,
274+
Module::UnresolvedHeaderDirective Header);
275+
276+
/// Look up the given header directive to find an actual header file.
261277
///
262278
/// \param M The module in which we're resolving the header directive.
263279
/// \param Header The header directive to resolve.
264280
/// \param RelativePathName Filled in with the relative path name from the
265281
/// module to the resolved header.
266282
/// \return The resolved file, if any.
267-
const FileEntry *resolveHeader(Module *M,
268-
Module::UnresolvedHeaderDirective Header,
269-
SmallVectorImpl<char> &RelativePathName);
283+
const FileEntry *findHeader(Module *M,
284+
const Module::UnresolvedHeaderDirective &Header,
285+
SmallVectorImpl<char> &RelativePathName);
286+
287+
/// Resolve the given header directive.
288+
void resolveHeader(Module *M,
289+
const Module::UnresolvedHeaderDirective &Header);
270290

271291
/// Attempt to resolve the specified header directive as naming a builtin
272292
/// header.
273-
const FileEntry *
274-
resolveAsBuiltinHeader(Module *M, Module::UnresolvedHeaderDirective Header,
275-
SmallVectorImpl<char> &BuiltinPathName);
293+
/// \return \c true if a corresponding builtin header was found.
294+
bool resolveAsBuiltinHeader(Module *M,
295+
const Module::UnresolvedHeaderDirective &Header);
276296

277297
/// \brief Looks up the modules that \p File corresponds to.
278298
///
@@ -368,6 +388,15 @@ class ModuleMap {
368388
/// the preferred module for the header.
369389
ArrayRef<KnownHeader> findAllModulesForHeader(const FileEntry *File) const;
370390

391+
/// Resolve all lazy header directives for the specified file.
392+
///
393+
/// This ensures that the HeaderFileInfo on HeaderSearch is up to date. This
394+
/// is effectively internal, but is exposed so HeaderSearch can call it.
395+
void resolveHeaderDirectives(const FileEntry *File) const;
396+
397+
/// Resolve all lazy header directives for the specified module.
398+
void resolveHeaderDirectives(Module *Mod) const;
399+
371400
/// \brief Reports errors if a module must not include a specific file.
372401
///
373402
/// \param RequestingModule The module including a file.

clang/lib/Basic/Module.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,11 +394,30 @@ void Module::print(raw_ostream &OS, unsigned Indent) const {
394394
{"exclude ", HK_Excluded}};
395395

396396
for (auto &K : Kinds) {
397+
assert(&K == &Kinds[K.Kind] && "kinds in wrong order");
397398
for (auto &H : Headers[K.Kind]) {
398399
OS.indent(Indent + 2);
399400
OS << K.Prefix << "header \"";
400401
OS.write_escaped(H.NameAsWritten);
401-
OS << "\"\n";
402+
OS << "\" { size " << H.Entry->getSize()
403+
<< " mtime " << H.Entry->getModificationTime() << " }\n";
404+
}
405+
}
406+
for (auto *Unresolved : {&UnresolvedHeaders, &MissingHeaders}) {
407+
for (auto &U : *Unresolved) {
408+
OS.indent(Indent + 2);
409+
OS << Kinds[U.Kind].Prefix << "header \"";
410+
OS.write_escaped(U.FileName);
411+
OS << "\"";
412+
if (U.Size || U.ModTime) {
413+
OS << " {";
414+
if (U.Size)
415+
OS << " size " << *U.Size;
416+
if (U.ModTime)
417+
OS << " mtime " << *U.ModTime;
418+
OS << " }";
419+
}
420+
OS << "\n";
402421
}
403422
}
404423

clang/lib/Frontend/FrontendAction.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -289,14 +289,28 @@ static void addHeaderInclude(StringRef HeaderName,
289289
///
290290
/// \param Includes Will be augmented with the set of \#includes or \#imports
291291
/// needed to load all of the named headers.
292-
static std::error_code
293-
collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr,
294-
ModuleMap &ModMap, clang::Module *Module,
295-
SmallVectorImpl<char> &Includes) {
292+
static std::error_code collectModuleHeaderIncludes(
293+
const LangOptions &LangOpts, FileManager &FileMgr, DiagnosticsEngine &Diag,
294+
ModuleMap &ModMap, clang::Module *Module, SmallVectorImpl<char> &Includes) {
296295
// Don't collect any headers for unavailable modules.
297296
if (!Module->isAvailable())
298297
return std::error_code();
299298

299+
// Resolve all lazy header directives to header files.
300+
ModMap.resolveHeaderDirectives(Module);
301+
302+
// If any headers are missing, we can't build this module. In most cases,
303+
// diagnostics for this should have already been produced; we only get here
304+
// if explicit stat information was provided.
305+
// FIXME: If the name resolves to a file with different stat information,
306+
// produce a better diagnostic.
307+
if (!Module->MissingHeaders.empty()) {
308+
auto &MissingHeader = Module->MissingHeaders.front();
309+
Diag.Report(MissingHeader.FileNameLoc, diag::err_module_header_missing)
310+
<< MissingHeader.IsUmbrella << MissingHeader.FileName;
311+
return std::error_code();
312+
}
313+
300314
// Add includes for each of these headers.
301315
for (auto HK : {Module::HK_Normal, Module::HK_Private}) {
302316
for (Module::Header &H : Module->Headers[HK]) {
@@ -367,7 +381,7 @@ collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr,
367381
SubEnd = Module->submodule_end();
368382
Sub != SubEnd; ++Sub)
369383
if (std::error_code Err = collectModuleHeaderIncludes(
370-
LangOpts, FileMgr, ModMap, *Sub, Includes))
384+
LangOpts, FileMgr, Diag, ModMap, *Sub, Includes))
371385
return Err;
372386

373387
return std::error_code();
@@ -494,7 +508,7 @@ getInputBufferForModule(CompilerInstance &CI, Module *M) {
494508
addHeaderInclude(UmbrellaHeader.NameAsWritten, HeaderContents,
495509
CI.getLangOpts(), M->IsExternC);
496510
Err = collectModuleHeaderIncludes(
497-
CI.getLangOpts(), FileMgr,
511+
CI.getLangOpts(), FileMgr, CI.getDiagnostics(),
498512
CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), M,
499513
HeaderContents);
500514

clang/lib/Lex/HeaderSearch.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,8 @@ bool HeaderSearch::ShouldEnterIncludeFile(Preprocessor &PP,
11141114
auto TryEnterImported = [&](void) -> bool {
11151115
if (!ModulesEnabled)
11161116
return false;
1117+
// Ensure FileInfo bits are up to date.
1118+
ModMap.resolveHeaderDirectives(File);
11171119
// Modules with builtins are special; multiple modules use builtins as
11181120
// modular headers, example:
11191121
//

0 commit comments

Comments
 (0)