r304515 - Support lazy stat'ing of files referenced by module maps.

Benjamin Kramer via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 2 10:31:01 PDT 2017


I committed a workaround in r304568.

On Fri, Jun 2, 2017 at 6:59 PM, Alexander Kornienko via cfe-commits
<cfe-commits at lists.llvm.org> wrote:
> I've not yet figured out exactly, but I have a suspicion that this commit
> causes crashes when run under asan. Specifically, when running
> test/Modules/preprocess-module.cpp
>
> The stack trace is:
>     #0 0x1d644c6 in (anonymous
> namespace)::HeaderFileInfoTrait::EmitData(llvm::raw_ostream&, (anonymous
> namespace)::HeaderFileInfoTrait::key_type const&, (anonymous
> namespace)::HeaderFileInfoTrait::data_type const&, unsigned int)
> llvm/tools/clang/lib/Serialization/ASTWriter.cpp:1926:39
>     #1 0x1d322f3 in llvm::OnDiskChainedHashTableGenerator<(anonymous
> namespace)::HeaderFileInfoTrait>::Emit(llvm::raw_ostream&, (anonymous
> namespace)::HeaderFileInfoTrait&)
> llvm/include/llvm/Support/OnDiskHashTable.h:198:17
>     #2 0x1d3168d in clang::ASTWriter::WriteHeaderSearch(clang::HeaderSearch
> const&) llvm/tools/clang/lib/Serialization/ASTWriter.cpp:2092:30
>     #3 0x1d52f4c in clang::ASTWriter::WriteASTCore(clang::Sema&,
> llvm::StringRef, std::string const&, clang::Module*)
> llvm/tools/clang/lib/Serialization/ASTWriter.cpp:4857:3
>     #4 0x1d4f7db in clang::ASTWriter::WriteAST(clang::Sema&, std::string
> const&, clang::Module*, llvm::StringRef, bool)
> llvm/tools/clang/lib/Serialization/ASTWriter.cpp:4475:7
>     #5 0x1cd0333 in
> clang::PCHGenerator::HandleTranslationUnit(clang::ASTContext&)
> llvm/tools/clang/lib/Serialization/GeneratePCH.cpp:62:14
>     #6 0x1f56cc7 in
> clang::MultiplexConsumer::HandleTranslationUnit(clang::ASTContext&)
> llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp:305:15
>     #7 0x24ebc9c in clang::ParseAST(clang::Sema&, bool, bool)
> llvm/tools/clang/lib/Parse/ParseAST.cpp:159:13
>     #8 0x1f4b283 in clang::FrontendAction::Execute()
> llvm/tools/clang/lib/Frontend/FrontendAction.cpp:856:8
>     #9 0x1caf916 in
> clang::CompilerInstance::ExecuteAction(clang::FrontendAction&)
> llvm/tools/clang/lib/Frontend/CompilerInstance.cpp:970:11
>     #10 0x5ed12c in
> clang::ExecuteCompilerInvocation(clang::CompilerInstance*)
> llvm/tools/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp:249:25
>
> Could you take a look?
>
>
> On Fri, Jun 2, 2017 at 3:55 AM, Richard Smith via cfe-commits
> <cfe-commits at lists.llvm.org> wrote:
>>
>> Author: rsmith
>> Date: Thu Jun  1 20:55:39 2017
>> New Revision: 304515
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=304515&view=rev
>> Log:
>> 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
>>
>> Added:
>>     cfe/trunk/test/Modules/Inputs/header-attribs/
>>     cfe/trunk/test/Modules/Inputs/header-attribs/bar.h
>>     cfe/trunk/test/Modules/Inputs/header-attribs/baz.h
>>     cfe/trunk/test/Modules/Inputs/header-attribs/foo.h
>>     cfe/trunk/test/Modules/Inputs/header-attribs/modular.modulemap
>>     cfe/trunk/test/Modules/Inputs/header-attribs/textual.modulemap
>>     cfe/trunk/test/Modules/header-attribs.cpp
>>     cfe/trunk/test/Modules/preprocess-missing.modulemap
>> Modified:
>>     cfe/trunk/docs/Modules.rst
>>     cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td
>>     cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
>>     cfe/trunk/include/clang/Basic/Module.h
>>     cfe/trunk/include/clang/Lex/ModuleMap.h
>>     cfe/trunk/lib/Basic/Module.cpp
>>     cfe/trunk/lib/Frontend/FrontendAction.cpp
>>     cfe/trunk/lib/Lex/HeaderSearch.cpp
>>     cfe/trunk/lib/Lex/ModuleMap.cpp
>>     cfe/trunk/lib/Lex/PPDirectives.cpp
>>     cfe/trunk/lib/Serialization/ASTWriter.cpp
>>     cfe/trunk/test/Modules/diagnostics.modulemap
>>     cfe/trunk/test/Modules/preprocess-module.cpp
>>
>> Modified: cfe/trunk/docs/Modules.rst
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/Modules.rst?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/docs/Modules.rst (original)
>> +++ cfe/trunk/docs/Modules.rst Thu Jun  1 20:55:39 2017
>> @@ -469,9 +469,16 @@ A header declaration specifies that a pa
>>  .. parsed-literal::
>>
>>    *header-declaration*:
>> -    ``private``:sub:`opt` ``textual``:sub:`opt` ``header``
>> *string-literal*
>> -    ``umbrella`` ``header`` *string-literal*
>> -    ``exclude`` ``header`` *string-literal*
>> +    ``private``:sub:`opt` ``textual``:sub:`opt` ``header``
>> *string-literal* *header-attrs*:sub:`opt`
>> +    ``umbrella`` ``header`` *string-literal* *header-attrs*:sub:`opt`
>> +    ``exclude`` ``header`` *string-literal* *header-attrs*:sub:`opt`
>> +
>> +  *header-attrs*:
>> +    '{' *header-attr** '}'
>> +
>> +  *header-attr*:
>> +    ``size`` *integer-literal*
>> +    ``mtime`` *integer-literal*
>>
>>  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.
>>
>> @@ -504,6 +511,18 @@ A header with the ``exclude`` specifier
>>
>>  A given header shall not be referenced by more than one
>> *header-declaration*.
>>
>> +Two *header-declaration*\s, or a *header-declaration* and a ``#include``,
>> are
>> +considered to refer to the same file if the paths resolve to the same
>> file
>> +and the specified *header-attr*\s (if any) match the attributes of that
>> file,
>> +even if the file is named differently (for instance, by a relative path
>> or
>> +via symlinks).
>> +
>> +.. note::
>> +    The use of *header-attr*\s avoids the need for Clang to speculatively
>> +    ``stat`` every header referenced by a module map. It is recommended
>> that
>> +    *header-attr*\s only be used in machine-generated module maps, to
>> avoid
>> +    mismatches between attribute values and the corresponding files.
>> +
>>  Umbrella directory declaration
>>  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>  An umbrella directory declaration specifies that all of the headers in
>> the specified directory should be included within the module.
>>
>> Modified: cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td (original)
>> +++ cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td Thu Jun  1
>> 20:55:39 2017
>> @@ -664,6 +664,12 @@ def warn_mmap_mismatched_top_level_priva
>>    InGroup<PrivateModule>;
>>  def note_mmap_rename_top_level_private_as_submodule : Note<
>>    "make '%0' a submodule of '%1' to ensure it can be found by name">;
>> +def err_mmap_duplicate_header_attribute : Error<
>> +  "header attribute '%0' specified multiple times">;
>> +def err_mmap_invalid_header_attribute_value : Error<
>> +  "expected integer literal as value for header attribute '%0'">;
>> +def err_mmap_expected_header_attribute : Error<
>> +  "expected a header attribute name ('size' or 'mtime')">;
>>
>>  def warn_auto_module_import : Warning<
>>    "treating #%select{include|import|include_next|__include_macros}0 as an
>> "
>>
>> Modified: cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
>> (original)
>> +++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td Thu Jun
>> 1 20:55:39 2017
>> @@ -174,10 +174,6 @@ def note_module_odr_violation_mismatch_d
>>    "method %2 with %ordinal3 parameter of type %4%select{| decayed from
>> %6}5|"
>>    "method %2 with %ordinal3 parameter named %4}1">;
>>
>> -def warn_module_uses_date_time : Warning<
>> -  "%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
>> -  InGroup<DiagGroup<"pch-date-time">>;
>> -
>>  def warn_duplicate_module_file_extension : Warning<
>>    "duplicate module file extension block name '%0'">,
>>    InGroup<ModuleFileExtension>;
>> @@ -186,7 +182,15 @@ def warn_module_system_bit_conflict : Wa
>>    "module file '%0' was validated as a system module and is now being
>> imported "
>>    "as a non-system module; any difference in diagnostic options will be
>> ignored">,
>>    InGroup<ModuleConflict>;
>> +} // let CategoryName
>>
>> +let CategoryName = "AST Serialization Issue" in {
>> +def warn_module_uses_date_time : Warning<
>> +  "%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
>> +  InGroup<DiagGroup<"pch-date-time">>;
>> +def err_module_no_size_mtime_for_header : Error<
>> +  "cannot emit module %0: %select{size|mtime}1 must be explicitly
>> specified "
>> +  "for missing header file \"%2\"">;
>>  } // let CategoryName
>>  } // let Component
>>
>>
>> Modified: cfe/trunk/include/clang/Basic/Module.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Module.h?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/include/clang/Basic/Module.h (original)
>> +++ cfe/trunk/include/clang/Basic/Module.h Thu Jun  1 20:55:39 2017
>> @@ -154,11 +154,19 @@ public:
>>    /// \brief Stored information about a header directive that was found
>> in the
>>    /// module map file but has not been resolved to a file.
>>    struct UnresolvedHeaderDirective {
>> +    HeaderKind Kind = HK_Normal;
>>      SourceLocation FileNameLoc;
>>      std::string FileName;
>> -    bool IsUmbrella;
>> +    bool IsUmbrella = false;
>> +    bool HasBuiltinHeader = false;
>> +    Optional<off_t> Size;
>> +    Optional<time_t> ModTime;
>>    };
>>
>> +  /// Headers that are mentioned in the module map file but that we have
>> not
>> +  /// yet attempted to resolve to a file on the file system.
>> +  SmallVector<UnresolvedHeaderDirective, 1> UnresolvedHeaders;
>> +
>>    /// \brief Headers that are mentioned in the module map file but could
>> not be
>>    /// found on the file system.
>>    SmallVector<UnresolvedHeaderDirective, 1> MissingHeaders;
>>
>> Modified: cfe/trunk/include/clang/Lex/ModuleMap.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/ModuleMap.h?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/include/clang/Lex/ModuleMap.h (original)
>> +++ cfe/trunk/include/clang/Lex/ModuleMap.h Thu Jun  1 20:55:39 2017
>> @@ -26,6 +26,7 @@
>>  #include "llvm/ADT/SmallVector.h"
>>  #include "llvm/ADT/StringMap.h"
>>  #include "llvm/ADT/StringRef.h"
>> +#include "llvm/ADT/TinyPtrVector.h"
>>  #include "llvm/ADT/Twine.h"
>>  #include <algorithm>
>>  #include <memory>
>> @@ -116,6 +117,11 @@ public:
>>      // Adjust ModuleMap::addHeader.
>>    };
>>
>> +  /// Convert a header kind to a role. Requires Kind to not be
>> HK_Excluded.
>> +  static ModuleHeaderRole headerKindToRole(Module::HeaderKind Kind);
>> +  /// Convert a header role to a kind.
>> +  static Module::HeaderKind headerRoleToKind(ModuleHeaderRole Role);
>> +
>>    /// \brief A header that is known to reside within a given module,
>>    /// whether it was included or excluded.
>>    class KnownHeader {
>> @@ -165,7 +171,13 @@ private:
>>    /// \brief Mapping from each header to the module that owns the
>> contents of
>>    /// that header.
>>    HeadersMap Headers;
>> -
>> +
>> +  /// Map from file sizes to modules with lazy header directives of that
>> size.
>> +  mutable llvm::DenseMap<off_t, llvm::TinyPtrVector<Module*>>
>> LazyHeadersBySize;
>> +  /// Map from mtimes to modules with lazy header directives with those
>> mtimes.
>> +  mutable llvm::DenseMap<time_t, llvm::TinyPtrVector<Module*>>
>> +              LazyHeadersByModTime;
>> +
>>    /// \brief Mapping from directories with umbrella headers to the module
>>    /// that is generated from the umbrella header.
>>    ///
>> @@ -257,22 +269,30 @@ private:
>>    /// resolved.
>>    Module *resolveModuleId(const ModuleId &Id, Module *Mod, bool Complain)
>> const;
>>
>> -  /// Resolve the given header directive to an actual header file.
>> +  /// Add an unresolved header to a module.
>> +  void addUnresolvedHeader(Module *Mod,
>> +                           Module::UnresolvedHeaderDirective Header);
>> +
>> +  /// Look up the given header directive to find an actual header file.
>>    ///
>>    /// \param M The module in which we're resolving the header directive.
>>    /// \param Header The header directive to resolve.
>>    /// \param RelativePathName Filled in with the relative path name from
>> the
>>    ///        module to the resolved header.
>>    /// \return The resolved file, if any.
>> -  const FileEntry *resolveHeader(Module *M,
>> -                                 Module::UnresolvedHeaderDirective
>> Header,
>> -                                 SmallVectorImpl<char>
>> &RelativePathName);
>> +  const FileEntry *findHeader(Module *M,
>> +                              const Module::UnresolvedHeaderDirective
>> &Header,
>> +                              SmallVectorImpl<char> &RelativePathName);
>> +
>> +  /// Resolve the given header directive.
>> +  void resolveHeader(Module *M,
>> +                     const Module::UnresolvedHeaderDirective &Header);
>>
>>    /// Attempt to resolve the specified header directive as naming a
>> builtin
>>    /// header.
>> -  const FileEntry *
>> -  resolveAsBuiltinHeader(Module *M, Module::UnresolvedHeaderDirective
>> Header,
>> -                         SmallVectorImpl<char> &BuiltinPathName);
>> +  /// \return \c true if a corresponding builtin header was found.
>> +  bool resolveAsBuiltinHeader(Module *M,
>> +                              const Module::UnresolvedHeaderDirective
>> &Header);
>>
>>    /// \brief Looks up the modules that \p File corresponds to.
>>    ///
>> @@ -368,6 +388,15 @@ public:
>>    /// the preferred module for the header.
>>    ArrayRef<KnownHeader> findAllModulesForHeader(const FileEntry *File)
>> const;
>>
>> +  /// Resolve all lazy header directives for the specified file.
>> +  ///
>> +  /// This ensures that the HeaderFileInfo on HeaderSearch is up to date.
>> This
>> +  /// is effectively internal, but is exposed so HeaderSearch can call
>> it.
>> +  void resolveHeaderDirectives(const FileEntry *File) const;
>> +
>> +  /// Resolve all lazy header directives for the specified module.
>> +  void resolveHeaderDirectives(Module *Mod) const;
>> +
>>    /// \brief Reports errors if a module must not include a specific file.
>>    ///
>>    /// \param RequestingModule The module including a file.
>>
>> Modified: cfe/trunk/lib/Basic/Module.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Module.cpp?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Basic/Module.cpp (original)
>> +++ cfe/trunk/lib/Basic/Module.cpp Thu Jun  1 20:55:39 2017
>> @@ -394,11 +394,30 @@ void Module::print(raw_ostream &OS, unsi
>>                 {"exclude ", HK_Excluded}};
>>
>>    for (auto &K : Kinds) {
>> +    assert(&K == &Kinds[K.Kind] && "kinds in wrong order");
>>      for (auto &H : Headers[K.Kind]) {
>>        OS.indent(Indent + 2);
>>        OS << K.Prefix << "header \"";
>>        OS.write_escaped(H.NameAsWritten);
>> -      OS << "\"\n";
>> +      OS << "\" { size " << H.Entry->getSize()
>> +         << " mtime " << H.Entry->getModificationTime() << " }\n";
>> +    }
>> +  }
>> +  for (auto *Unresolved : {&UnresolvedHeaders, &MissingHeaders}) {
>> +    for (auto &U : *Unresolved) {
>> +      OS.indent(Indent + 2);
>> +      OS << Kinds[U.Kind].Prefix << "header \"";
>> +      OS.write_escaped(U.FileName);
>> +      OS << "\"";
>> +      if (U.Size || U.ModTime) {
>> +        OS << " {";
>> +        if (U.Size)
>> +          OS << " size " << *U.Size;
>> +        if (U.ModTime)
>> +          OS << " mtime " << *U.ModTime;
>> +        OS << " }";
>> +      }
>> +      OS << "\n";
>>      }
>>    }
>>
>>
>> Modified: cfe/trunk/lib/Frontend/FrontendAction.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/FrontendAction.cpp?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Frontend/FrontendAction.cpp (original)
>> +++ cfe/trunk/lib/Frontend/FrontendAction.cpp Thu Jun  1 20:55:39 2017
>> @@ -289,14 +289,28 @@ static void addHeaderInclude(StringRef H
>>  ///
>>  /// \param Includes Will be augmented with the set of \#includes or
>> \#imports
>>  /// needed to load all of the named headers.
>> -static std::error_code
>> -collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager
>> &FileMgr,
>> -                            ModuleMap &ModMap, clang::Module *Module,
>> -                            SmallVectorImpl<char> &Includes) {
>> +static std::error_code collectModuleHeaderIncludes(
>> +    const LangOptions &LangOpts, FileManager &FileMgr, DiagnosticsEngine
>> &Diag,
>> +    ModuleMap &ModMap, clang::Module *Module, SmallVectorImpl<char>
>> &Includes) {
>>    // Don't collect any headers for unavailable modules.
>>    if (!Module->isAvailable())
>>      return std::error_code();
>>
>> +  // Resolve all lazy header directives to header files.
>> +  ModMap.resolveHeaderDirectives(Module);
>> +
>> +  // If any headers are missing, we can't build this module. In most
>> cases,
>> +  // diagnostics for this should have already been produced; we only get
>> here
>> +  // if explicit stat information was provided.
>> +  // FIXME: If the name resolves to a file with different stat
>> information,
>> +  // produce a better diagnostic.
>> +  if (!Module->MissingHeaders.empty()) {
>> +    auto &MissingHeader = Module->MissingHeaders.front();
>> +    Diag.Report(MissingHeader.FileNameLoc,
>> diag::err_module_header_missing)
>> +      << MissingHeader.IsUmbrella << MissingHeader.FileName;
>> +    return std::error_code();
>> +  }
>> +
>>    // Add includes for each of these headers.
>>    for (auto HK : {Module::HK_Normal, Module::HK_Private}) {
>>      for (Module::Header &H : Module->Headers[HK]) {
>> @@ -367,7 +381,7 @@ collectModuleHeaderIncludes(const LangOp
>>                                        SubEnd = Module->submodule_end();
>>         Sub != SubEnd; ++Sub)
>>      if (std::error_code Err = collectModuleHeaderIncludes(
>> -            LangOpts, FileMgr, ModMap, *Sub, Includes))
>> +            LangOpts, FileMgr, Diag, ModMap, *Sub, Includes))
>>        return Err;
>>
>>    return std::error_code();
>> @@ -494,7 +508,7 @@ getInputBufferForModule(CompilerInstance
>>      addHeaderInclude(UmbrellaHeader.NameAsWritten, HeaderContents,
>>                       CI.getLangOpts(), M->IsExternC);
>>    Err = collectModuleHeaderIncludes(
>> -      CI.getLangOpts(), FileMgr,
>> +      CI.getLangOpts(), FileMgr, CI.getDiagnostics(),
>>        CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), M,
>>        HeaderContents);
>>
>>
>> Modified: cfe/trunk/lib/Lex/HeaderSearch.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/HeaderSearch.cpp?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Lex/HeaderSearch.cpp (original)
>> +++ cfe/trunk/lib/Lex/HeaderSearch.cpp Thu Jun  1 20:55:39 2017
>> @@ -1114,6 +1114,8 @@ bool HeaderSearch::ShouldEnterIncludeFil
>>    auto TryEnterImported = [&](void) -> bool {
>>      if (!ModulesEnabled)
>>        return false;
>> +    // Ensure FileInfo bits are up to date.
>> +    ModMap.resolveHeaderDirectives(File);
>>      // Modules with builtins are special; multiple modules use builtins
>> as
>>      // modular headers, example:
>>      //
>>
>> Modified: cfe/trunk/lib/Lex/ModuleMap.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/ModuleMap.cpp?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Lex/ModuleMap.cpp (original)
>> +++ cfe/trunk/lib/Lex/ModuleMap.cpp Thu Jun  1 20:55:39 2017
>> @@ -36,6 +36,37 @@
>>  #endif
>>  using namespace clang;
>>
>> +Module::HeaderKind ModuleMap::headerRoleToKind(ModuleHeaderRole Role) {
>> +  switch ((int)Role) {
>> +  default: llvm_unreachable("unknown header role");
>> +  case NormalHeader:
>> +    return Module::HK_Normal;
>> +  case PrivateHeader:
>> +    return Module::HK_Private;
>> +  case TextualHeader:
>> +    return Module::HK_Textual;
>> +  case PrivateHeader | TextualHeader:
>> +    return Module::HK_PrivateTextual;
>> +  }
>> +}
>> +
>> +ModuleMap::ModuleHeaderRole
>> +ModuleMap::headerKindToRole(Module::HeaderKind Kind) {
>> +  switch ((int)Kind) {
>> +  case Module::HK_Normal:
>> +    return NormalHeader;
>> +  case Module::HK_Private:
>> +    return PrivateHeader;
>> +  case Module::HK_Textual:
>> +    return TextualHeader;
>> +  case Module::HK_PrivateTextual:
>> +    return ModuleHeaderRole(PrivateHeader | TextualHeader);
>> +  case Module::HK_Excluded:
>> +    llvm_unreachable("unexpected header kind");
>> +  }
>> +  llvm_unreachable("unknown header kind");
>> +}
>> +
>>  Module::ExportDecl
>>  ModuleMap::resolveExport(Module *Mod,
>>                           const Module::UnresolvedExportDecl &Unresolved,
>> @@ -104,12 +135,22 @@ static void appendSubframeworkPaths(Modu
>>  }
>>
>>  const FileEntry *
>> -ModuleMap::resolveHeader(Module *M, Module::UnresolvedHeaderDirective
>> Header,
>> -                         SmallVectorImpl<char> &RelativePathName) {
>> +ModuleMap::findHeader(Module *M,
>> +                      const Module::UnresolvedHeaderDirective &Header,
>> +                      SmallVectorImpl<char> &RelativePathName) {
>> +  auto GetFile = [&](StringRef Filename) -> const FileEntry * {
>> +    auto *File = SourceMgr.getFileManager().getFile(Filename);
>> +    if (!File ||
>> +        (Header.Size && File->getSize() != *Header.Size) ||
>> +        (Header.ModTime && File->getModificationTime() !=
>> *Header.ModTime))
>> +      return nullptr;
>> +    return File;
>> +  };
>> +
>>    if (llvm::sys::path::is_absolute(Header.FileName)) {
>>      RelativePathName.clear();
>>      RelativePathName.append(Header.FileName.begin(),
>> Header.FileName.end());
>> -    return SourceMgr.getFileManager().getFile(Header.FileName);
>> +    return GetFile(Header.FileName);
>>    }
>>
>>    // Search for the header file within the module's home directory.
>> @@ -124,7 +165,7 @@ ModuleMap::resolveHeader(Module *M, Modu
>>      // Check whether this file is in the public headers.
>>      llvm::sys::path::append(RelativePathName, "Headers",
>> Header.FileName);
>>      llvm::sys::path::append(FullPathName, RelativePathName);
>> -    if (auto *File = SourceMgr.getFileManager().getFile(FullPathName))
>> +    if (auto *File = GetFile(FullPathName))
>>        return File;
>>
>>      // Check whether this file is in the private headers.
>> @@ -141,31 +182,74 @@ ModuleMap::resolveHeader(Module *M, Modu
>>      llvm::sys::path::append(RelativePathName, "PrivateHeaders",
>>                              Header.FileName);
>>      llvm::sys::path::append(FullPathName, RelativePathName);
>> -    return SourceMgr.getFileManager().getFile(FullPathName);
>> +    return GetFile(FullPathName);
>>    }
>>
>>    // Lookup for normal headers.
>>    llvm::sys::path::append(RelativePathName, Header.FileName);
>>    llvm::sys::path::append(FullPathName, RelativePathName);
>> -  return SourceMgr.getFileManager().getFile(FullPathName);
>> +  return GetFile(FullPathName);
>>  }
>>
>> -const FileEntry *
>> -ModuleMap::resolveAsBuiltinHeader(Module *M,
>> -                                  Module::UnresolvedHeaderDirective
>> Header,
>> -                                  SmallVectorImpl<char> &BuiltinPathName)
>> {
>> -  if (llvm::sys::path::is_absolute(Header.FileName) ||
>> M->isPartOfFramework() ||
>> -      !M->IsSystem || Header.IsUmbrella || !BuiltinIncludeDir ||
>> -      BuiltinIncludeDir == M->Directory ||
>> !isBuiltinHeader(Header.FileName))
>> -    return nullptr;
>> +void ModuleMap::resolveHeader(Module *Mod,
>> +                              const Module::UnresolvedHeaderDirective
>> &Header) {
>> +  SmallString<128> RelativePathName;
>> +  if (const FileEntry *File = findHeader(Mod, Header, RelativePathName))
>> {
>> +    if (Header.IsUmbrella) {
>> +      const DirectoryEntry *UmbrellaDir = File->getDir();
>> +      if (Module *UmbrellaMod = UmbrellaDirs[UmbrellaDir])
>> +        Diags.Report(Header.FileNameLoc, diag::err_mmap_umbrella_clash)
>> +          << UmbrellaMod->getFullModuleName();
>> +      else
>> +        // Record this umbrella header.
>> +        setUmbrellaHeader(Mod, File, RelativePathName.str());
>> +    } else {
>> +      Module::Header H = {RelativePathName.str(), File};
>> +      if (Header.Kind == Module::HK_Excluded)
>> +        excludeHeader(Mod, H);
>> +      else
>> +        addHeader(Mod, H, headerKindToRole(Header.Kind));
>> +    }
>> +  } else if (Header.HasBuiltinHeader && !Header.Size && !Header.ModTime)
>> {
>> +    // There's a builtin header but no corresponding on-disk header.
>> Assume
>> +    // this was supposed to modularize the builtin header alone.
>> +  } else if (Header.Kind == Module::HK_Excluded) {
>> +    // Ignore missing excluded header files. They're optional anyway.
>> +  } else {
>> +    // If we find a module that has a missing header, we mark this module
>> as
>> +    // unavailable and store the header directive for displaying
>> diagnostics.
>> +    Mod->MissingHeaders.push_back(Header);
>> +    // A missing header with stat information doesn't make the module
>> +    // unavailable; this keeps our behavior consistent as headers are
>> lazily
>> +    // resolved. (Such a module still can't be built though, except from
>> +    // preprocessed source.)
>> +    if (!Header.Size && !Header.ModTime)
>> +      Mod->markUnavailable();
>> +  }
>> +}
>> +
>> +bool ModuleMap::resolveAsBuiltinHeader(
>> +    Module *Mod, const Module::UnresolvedHeaderDirective &Header) {
>> +  if (Header.Kind == Module::HK_Excluded ||
>> +      llvm::sys::path::is_absolute(Header.FileName) ||
>> +      Mod->isPartOfFramework() || !Mod->IsSystem || Header.IsUmbrella ||
>> +      !BuiltinIncludeDir || BuiltinIncludeDir == Mod->Directory ||
>> +      !isBuiltinHeader(Header.FileName))
>> +    return false;
>>
>>    // This is a system module with a top-level header. This header
>>    // may have a counterpart (or replacement) in the set of headers
>>    // supplied by Clang. Find that builtin header.
>> -  llvm::sys::path::append(BuiltinPathName, BuiltinIncludeDir->getName(),
>> -                          Header.FileName);
>> -  return SourceMgr.getFileManager().getFile(
>> -      StringRef(BuiltinPathName.data(), BuiltinPathName.size()));
>> +  SmallString<128> Path;
>> +  llvm::sys::path::append(Path, BuiltinIncludeDir->getName(),
>> Header.FileName);
>> +  auto *File = SourceMgr.getFileManager().getFile(Path);
>> +  if (!File)
>> +    return false;
>> +
>> +  auto Role = headerKindToRole(Header.Kind);
>> +  Module::Header H = {Path.str(), File};
>> +  addHeader(Mod, H, Role);
>> +  return true;
>>  }
>>
>>  ModuleMap::ModuleMap(SourceManager &SourceMgr, DiagnosticsEngine &Diags,
>> @@ -246,6 +330,7 @@ bool ModuleMap::isBuiltinHeader(StringRe
>>
>>  ModuleMap::HeadersMap::iterator
>>  ModuleMap::findKnownHeader(const FileEntry *File) {
>> +  resolveHeaderDirectives(File);
>>    HeadersMap::iterator Known = Headers.find(File);
>>    if (HeaderInfo.getHeaderSearchOpts().ImplicitModuleMaps &&
>>        Known == Headers.end() && File->getDir() == BuiltinIncludeDir &&
>> @@ -328,8 +413,10 @@ void ModuleMap::diagnoseHeaderInclusion(
>>    if (getTopLevelOrNull(RequestingModule) !=
>> getTopLevelOrNull(SourceModule))
>>      return;
>>
>> -  if (RequestingModule)
>> +  if (RequestingModule) {
>>      resolveUses(RequestingModule, /*Complain=*/false);
>> +    resolveHeaderDirectives(RequestingModule);
>> +  }
>>
>>    bool Excluded = false;
>>    Module *Private = nullptr;
>> @@ -511,6 +598,7 @@ ModuleMap::findOrCreateModuleForHeaderIn
>>
>>  ArrayRef<ModuleMap::KnownHeader>
>>  ModuleMap::findAllModulesForHeader(const FileEntry *File) const {
>> +  resolveHeaderDirectives(File);
>>    auto It = Headers.find(File);
>>    if (It == Headers.end())
>>      return None;
>> @@ -524,6 +612,7 @@ bool ModuleMap::isHeaderInUnavailableMod
>>  bool
>>  ModuleMap::isHeaderUnavailableInModule(const FileEntry *Header,
>>                                         const Module *RequestingModule)
>> const {
>> +  resolveHeaderDirectives(Header);
>>    HeadersMap::const_iterator Known = Headers.find(Header);
>>    if (Known != Headers.end()) {
>>      for (SmallVectorImpl<KnownHeader>::const_iterator
>> @@ -896,20 +985,65 @@ void ModuleMap::setUmbrellaDir(Module *M
>>    UmbrellaDirs[UmbrellaDir] = Mod;
>>  }
>>
>> -static Module::HeaderKind headerRoleToKind(ModuleMap::ModuleHeaderRole
>> Role) {
>> -  switch ((int)Role) {
>> -  default: llvm_unreachable("unknown header role");
>> -  case ModuleMap::NormalHeader:
>> -    return Module::HK_Normal;
>> -  case ModuleMap::PrivateHeader:
>> -    return Module::HK_Private;
>> -  case ModuleMap::TextualHeader:
>> -    return Module::HK_Textual;
>> -  case ModuleMap::PrivateHeader | ModuleMap::TextualHeader:
>> -    return Module::HK_PrivateTextual;
>> +void ModuleMap::addUnresolvedHeader(Module *Mod,
>> +                                    Module::UnresolvedHeaderDirective
>> Header) {
>> +  // If there is a builtin counterpart to this file, add it now so it can
>> +  // wrap the system header.
>> +  if (resolveAsBuiltinHeader(Mod, Header)) {
>> +    // If we have both a builtin and system version of the file, the
>> +    // builtin version may want to inject macros into the system header,
>> so
>> +    // force the system header to be treated as a textual header in this
>> +    // case.
>> +    Header.Kind = headerRoleToKind(ModuleMap::ModuleHeaderRole(
>> +        headerKindToRole(Header.Kind) | ModuleMap::TextualHeader));
>> +    Header.HasBuiltinHeader = true;
>> +  }
>> +
>> +  // If possible, don't stat the header until we need to. This requires
>> the
>> +  // user to have provided us with some stat information about the file.
>> +  // FIXME: Add support for lazily stat'ing umbrella headers and excluded
>> +  // headers.
>> +  if ((Header.Size || Header.ModTime) && !Header.IsUmbrella &&
>> +      Header.Kind != Module::HK_Excluded) {
>> +    // We expect more variation in mtime than size, so if we're given
>> both,
>> +    // use the mtime as the key.
>> +    if (Header.ModTime)
>> +      LazyHeadersByModTime[*Header.ModTime].push_back(Mod);
>> +    else
>> +      LazyHeadersBySize[*Header.Size].push_back(Mod);
>> +    Mod->UnresolvedHeaders.push_back(Header);
>> +    return;
>> +  }
>> +
>> +  // We don't have stat information or can't defer looking this file up.
>> +  // Perform the lookup now.
>> +  resolveHeader(Mod, Header);
>> +}
>> +
>> +void ModuleMap::resolveHeaderDirectives(const FileEntry *File) const {
>> +  auto BySize = LazyHeadersBySize.find(File->getSize());
>> +  if (BySize != LazyHeadersBySize.end()) {
>> +    for (auto *M : BySize->second)
>> +      resolveHeaderDirectives(M);
>> +    LazyHeadersBySize.erase(BySize);
>> +  }
>> +
>> +  auto ByModTime =
>> LazyHeadersByModTime.find(File->getModificationTime());
>> +  if (ByModTime != LazyHeadersByModTime.end()) {
>> +    for (auto *M : ByModTime->second)
>> +      resolveHeaderDirectives(M);
>> +    LazyHeadersByModTime.erase(ByModTime);
>>    }
>>  }
>>
>> +void ModuleMap::resolveHeaderDirectives(Module *Mod) const {
>> +  for (auto &Header : Mod->UnresolvedHeaders)
>> +    // This operation is logically const; we're just changing how we
>> represent
>> +    // the header information for this file.
>> +    const_cast<ModuleMap*>(this)->resolveHeader(Mod, Header);
>> +  Mod->UnresolvedHeaders.clear();
>> +}
>> +
>>  void ModuleMap::addHeader(Module *Mod, Module::Header Header,
>>                            ModuleHeaderRole Role, bool Imported) {
>>    KnownHeader KH(Mod, Role);
>> @@ -1063,6 +1197,7 @@ namespace clang {
>>        RequiresKeyword,
>>        Star,
>>        StringLiteral,
>> +      IntegerLiteral,
>>        TextualKeyword,
>>        LBrace,
>>        RBrace,
>> @@ -1072,7 +1207,12 @@ namespace clang {
>>
>>      unsigned Location;
>>      unsigned StringLength;
>> -    const char *StringData;
>> +    union {
>> +      // If Kind != IntegerLiteral.
>> +      const char *StringData;
>> +      // If Kind == IntegerLiteral.
>> +      uint64_t IntegerValue;
>> +    };
>>
>>      void clear() {
>>        Kind = EndOfFile;
>> @@ -1086,9 +1226,14 @@ namespace clang {
>>      SourceLocation getLocation() const {
>>        return SourceLocation::getFromRawEncoding(Location);
>>      }
>> +
>> +    uint64_t getInteger() const {
>> +      return Kind == IntegerLiteral ? IntegerValue : 0;
>> +    }
>>
>>      StringRef getString() const {
>> -      return StringRef(StringData, StringLength);
>> +      return Kind == IntegerLiteral ? StringRef()
>> +                                    : StringRef(StringData,
>> StringLength);
>>      }
>>    };
>>
>> @@ -1278,6 +1423,25 @@ retry:
>>      Tok.StringLength = Length;
>>      break;
>>    }
>> +
>> +  case tok::numeric_constant: {
>> +    // We don't support any suffixes or other complications.
>> +    SmallString<32> SpellingBuffer;
>> +    SpellingBuffer.resize(LToken.getLength() + 1);
>> +    const char *Start = SpellingBuffer.data();
>> +    unsigned Length =
>> +        Lexer::getSpelling(LToken, Start, SourceMgr, L.getLangOpts());
>> +    uint64_t Value;
>> +    if (StringRef(Start, Length).getAsInteger(0, Value)) {
>> +      Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token);
>> +      HadError = true;
>> +      goto retry;
>> +    }
>> +
>> +    Tok.Kind = MMToken::IntegerLiteral;
>> +    Tok.IntegerValue = Value;
>> +    break;
>> +  }
>>
>>    case tok::comment:
>>      goto retry;
>> @@ -1904,6 +2068,9 @@ void ModuleMapParser::parseHeaderDecl(MM
>>    Header.FileName = Tok.getString();
>>    Header.FileNameLoc = consumeToken();
>>    Header.IsUmbrella = LeadingToken == MMToken::UmbrellaKeyword;
>> +  Header.Kind =
>> +      (LeadingToken == MMToken::ExcludeKeyword ? Module::HK_Excluded
>> +                                               :
>> Map.headerRoleToKind(Role));
>>
>>    // Check whether we already have an umbrella.
>>    if (Header.IsUmbrella && ActiveModule->Umbrella) {
>> @@ -1913,64 +2080,62 @@ void ModuleMapParser::parseHeaderDecl(MM
>>      return;
>>    }
>>
>> -  // Look for this file by name if we don't have any stat information.
>> -  SmallString<128> RelativePathName, BuiltinPathName;
>> -  const FileEntry *File =
>> -      Map.resolveHeader(ActiveModule, Header, RelativePathName);
>> -  const FileEntry *BuiltinFile =
>> -      Map.resolveAsBuiltinHeader(ActiveModule, Header, BuiltinPathName);
>> -
>> -  // If Clang supplies this header but the underlying system does not,
>> -  // just silently swap in our builtin version. Otherwise, we'll end
>> -  // up adding both (later).
>> -  if (BuiltinFile && !File) {
>> -    RelativePathName = BuiltinPathName;
>> -    File = BuiltinFile;
>> -    BuiltinFile = nullptr;
>> -  }
>> -
>> -  // FIXME: We shouldn't be eagerly stat'ing every file named in a module
>> map.
>> -  // Come up with a lazy way to do this.
>> -  if (File) {
>> -    if (Header.IsUmbrella) {
>> -      const DirectoryEntry *UmbrellaDir = File->getDir();
>> -      if (Module *UmbrellaModule = Map.UmbrellaDirs[UmbrellaDir]) {
>> -        Diags.Report(LeadingLoc, diag::err_mmap_umbrella_clash)
>> -          << UmbrellaModule->getFullModuleName();
>> -        HadError = true;
>> -      } else {
>> -        // Record this umbrella header.
>> -        Map.setUmbrellaHeader(ActiveModule, File,
>> RelativePathName.str());
>> -      }
>> -    } else if (LeadingToken == MMToken::ExcludeKeyword) {
>> -      Module::Header H = {RelativePathName.str(), File};
>> -      Map.excludeHeader(ActiveModule, H);
>> -    } else {
>> -      // If there is a builtin counterpart to this file, add it now so it
>> can
>> -      // wrap the system header.
>> -      if (BuiltinFile) {
>> -        Module::Header H = { BuiltinPathName.str(), BuiltinFile };
>> -        Map.addHeader(ActiveModule, H, Role);
>> -
>> -        // If we have both a builtin and system version of the file, the
>> -        // builtin version may want to inject macros into the system
>> header, so
>> -        // force the system header to be treated as a textual header in
>> this
>> -        // case.
>> -        Role = ModuleMap::ModuleHeaderRole(Role |
>> ModuleMap::TextualHeader);
>> -      }
>> +  // If we were given stat information, parse it so we can skip looking
>> for
>> +  // the file.
>> +  if (Tok.is(MMToken::LBrace)) {
>> +    SourceLocation LBraceLoc = consumeToken();
>> +
>> +    while (!Tok.is(MMToken::RBrace) && !Tok.is(MMToken::EndOfFile)) {
>> +      enum Attribute { Size, ModTime, Unknown };
>> +      StringRef Str = Tok.getString();
>> +      SourceLocation Loc = consumeToken();
>> +      switch (llvm::StringSwitch<Attribute>(Str)
>> +                  .Case("size", Size)
>> +                  .Case("mtime", ModTime)
>> +                  .Default(Unknown)) {
>> +      case Size:
>> +        if (Header.Size)
>> +          Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) <<
>> Str;
>> +        if (!Tok.is(MMToken::IntegerLiteral)) {
>> +          Diags.Report(Tok.getLocation(),
>> +                       diag::err_mmap_invalid_header_attribute_value) <<
>> Str;
>> +          skipUntil(MMToken::RBrace);
>> +          break;
>> +        }
>> +        Header.Size = Tok.getInteger();
>> +        consumeToken();
>> +        break;
>>
>> -      // Record this header.
>> -      Module::Header H = { RelativePathName.str(), File };
>> -      Map.addHeader(ActiveModule, H, Role);
>> +      case ModTime:
>> +        if (Header.ModTime)
>> +          Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) <<
>> Str;
>> +        if (!Tok.is(MMToken::IntegerLiteral)) {
>> +          Diags.Report(Tok.getLocation(),
>> +                       diag::err_mmap_invalid_header_attribute_value) <<
>> Str;
>> +          skipUntil(MMToken::RBrace);
>> +          break;
>> +        }
>> +        Header.ModTime = Tok.getInteger();
>> +        consumeToken();
>> +        break;
>> +
>> +      case Unknown:
>> +        Diags.Report(Loc, diag::err_mmap_expected_header_attribute);
>> +        skipUntil(MMToken::RBrace);
>> +        break;
>> +      }
>>      }
>> -  } else if (LeadingToken != MMToken::ExcludeKeyword) {
>> -    // Ignore excluded header files. They're optional anyway.
>>
>> -    // If we find a module that has a missing header, we mark this module
>> as
>> -    // unavailable and store the header directive for displaying
>> diagnostics.
>> -    ActiveModule->markUnavailable();
>> -    ActiveModule->MissingHeaders.push_back(Header);
>> +    if (Tok.is(MMToken::RBrace))
>> +      consumeToken();
>> +    else {
>> +      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace);
>> +      Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match);
>> +      HadError = true;
>> +    }
>>    }
>> +
>> +  Map.addUnresolvedHeader(ActiveModule, std::move(Header));
>>  }
>>
>>  static int compareModuleHeaders(const Module::Header *A,
>> @@ -2521,6 +2686,7 @@ bool ModuleMapParser::parseModuleMapFile
>>      case MMToken::RequiresKeyword:
>>      case MMToken::Star:
>>      case MMToken::StringLiteral:
>> +    case MMToken::IntegerLiteral:
>>      case MMToken::TextualKeyword:
>>      case MMToken::UmbrellaKeyword:
>>      case MMToken::UseKeyword:
>>
>> Modified: cfe/trunk/lib/Lex/PPDirectives.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPDirectives.cpp?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Lex/PPDirectives.cpp (original)
>> +++ cfe/trunk/lib/Lex/PPDirectives.cpp Thu Jun  1 20:55:39 2017
>> @@ -689,6 +689,8 @@ Preprocessor::getModuleHeaderToIncludeFo
>>    while (!Loc.isInvalid() && !SM.isInMainFile(Loc)) {
>>      auto ID = SM.getFileID(SM.getExpansionLoc(Loc));
>>      auto *FE = SM.getFileEntryForID(ID);
>> +    if (!FE)
>> +      break;
>>
>>      bool InTextualHeader = false;
>>      for (auto Header :
>> HeaderInfo.getModuleMap().findAllModulesForHeader(FE)) {
>>
>> Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
>> +++ cfe/trunk/lib/Serialization/ASTWriter.cpp Thu Jun  1 20:55:39 2017
>> @@ -1856,24 +1856,31 @@ namespace {
>>    // Trait used for the on-disk hash table of header search information.
>>    class HeaderFileInfoTrait {
>>      ASTWriter &Writer;
>> -    const HeaderSearch &HS;
>>
>>      // Keep track of the framework names we've used during serialization.
>>      SmallVector<char, 128> FrameworkStringData;
>>      llvm::StringMap<unsigned> FrameworkNameOffset;
>>
>>    public:
>> -    HeaderFileInfoTrait(ASTWriter &Writer, const HeaderSearch &HS)
>> -      : Writer(Writer), HS(HS) { }
>> -
>> +    HeaderFileInfoTrait(ASTWriter &Writer) : Writer(Writer) {}
>> +
>>      struct key_type {
>> -      const FileEntry *FE;
>>        StringRef Filename;
>> +      off_t Size;
>> +      time_t ModTime;
>>      };
>>      typedef const key_type &key_type_ref;
>> +
>> +    using UnresolvedModule =
>> +        llvm::PointerIntPair<Module *, 2, ModuleMap::ModuleHeaderRole>;
>>
>> -    typedef HeaderFileInfo data_type;
>> +    struct data_type {
>> +      const HeaderFileInfo &HFI;
>> +      ArrayRef<ModuleMap::KnownHeader> KnownHeaders;
>> +      UnresolvedModule Unresolved;
>> +    };
>>      typedef const data_type &data_type_ref;
>> +
>>      typedef unsigned hash_value_type;
>>      typedef unsigned offset_type;
>>
>> @@ -1881,8 +1888,7 @@ namespace {
>>        // The hash is based only on size/time of the file, so that the
>> reader can
>>        // match even when symlinking or excess path elements ("foo/../",
>> "../")
>>        // change the form of the name. However, complete path is still the
>> key.
>> -      return llvm::hash_combine(key.FE->getSize(),
>> -                                Writer.getTimestampForOutput(key.FE));
>> +      return llvm::hash_combine(key.Size, key.ModTime);
>>      }
>>
>>      std::pair<unsigned,unsigned>
>> @@ -1892,68 +1898,74 @@ namespace {
>>        unsigned KeyLen = key.Filename.size() + 1 + 8 + 8;
>>        LE.write<uint16_t>(KeyLen);
>>        unsigned DataLen = 1 + 2 + 4 + 4;
>> -      for (auto ModInfo :
>> HS.getModuleMap().findAllModulesForHeader(key.FE))
>> +      for (auto ModInfo : Data.KnownHeaders)
>>          if (Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule()))
>>            DataLen += 4;
>> +      if (Data.Unresolved.getPointer())
>> +        DataLen += 4;
>>        LE.write<uint8_t>(DataLen);
>>        return std::make_pair(KeyLen, DataLen);
>>      }
>> -
>> +
>>      void EmitKey(raw_ostream& Out, key_type_ref key, unsigned KeyLen) {
>>        using namespace llvm::support;
>>        endian::Writer<little> LE(Out);
>> -      LE.write<uint64_t>(key.FE->getSize());
>> +      LE.write<uint64_t>(key.Size);
>>        KeyLen -= 8;
>> -      LE.write<uint64_t>(Writer.getTimestampForOutput(key.FE));
>> +      LE.write<uint64_t>(key.ModTime);
>>        KeyLen -= 8;
>>        Out.write(key.Filename.data(), KeyLen);
>>      }
>> -
>> +
>>      void EmitData(raw_ostream &Out, key_type_ref key,
>>                    data_type_ref Data, unsigned DataLen) {
>>        using namespace llvm::support;
>>        endian::Writer<little> LE(Out);
>>        uint64_t Start = Out.tell(); (void)Start;
>>
>> -      unsigned char Flags = (Data.isImport << 4)
>> -                          | (Data.isPragmaOnce << 3)
>> -                          | (Data.DirInfo << 1)
>> -                          | Data.IndexHeaderMapHeader;
>> +      unsigned char Flags = (Data.HFI.isImport << 4)
>> +                          | (Data.HFI.isPragmaOnce << 3)
>> +                          | (Data.HFI.DirInfo << 1)
>> +                          | Data.HFI.IndexHeaderMapHeader;
>>        LE.write<uint8_t>(Flags);
>> -      LE.write<uint16_t>(Data.NumIncludes);
>> +      LE.write<uint16_t>(Data.HFI.NumIncludes);
>>
>> -      if (!Data.ControllingMacro)
>> -        LE.write<uint32_t>(Data.ControllingMacroID);
>> +      if (!Data.HFI.ControllingMacro)
>> +        LE.write<uint32_t>(Data.HFI.ControllingMacroID);
>>        else
>> -
>> LE.write<uint32_t>(Writer.getIdentifierRef(Data.ControllingMacro));
>> -
>> +
>> LE.write<uint32_t>(Writer.getIdentifierRef(Data.HFI.ControllingMacro));
>> +
>>        unsigned Offset = 0;
>> -      if (!Data.Framework.empty()) {
>> +      if (!Data.HFI.Framework.empty()) {
>>          // If this header refers into a framework, save the framework
>> name.
>>          llvm::StringMap<unsigned>::iterator Pos
>> -          = FrameworkNameOffset.find(Data.Framework);
>> +          = FrameworkNameOffset.find(Data.HFI.Framework);
>>          if (Pos == FrameworkNameOffset.end()) {
>>            Offset = FrameworkStringData.size() + 1;
>> -          FrameworkStringData.append(Data.Framework.begin(),
>> -                                     Data.Framework.end());
>> +          FrameworkStringData.append(Data.HFI.Framework.begin(),
>> +                                     Data.HFI.Framework.end());
>>            FrameworkStringData.push_back(0);
>>
>> -          FrameworkNameOffset[Data.Framework] = Offset;
>> +          FrameworkNameOffset[Data.HFI.Framework] = Offset;
>>          } else
>>            Offset = Pos->second;
>>        }
>>        LE.write<uint32_t>(Offset);
>>
>> -      // FIXME: If the header is excluded, we should write out some
>> -      // record of that fact.
>> -      for (auto ModInfo :
>> HS.getModuleMap().findAllModulesForHeader(key.FE)) {
>> -        if (uint32_t ModID =
>> -
>> Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule())) {
>> -          uint32_t Value = (ModID << 2) | (unsigned)ModInfo.getRole();
>> +      auto EmitModule = [&](Module *M, ModuleMap::ModuleHeaderRole Role)
>> {
>> +        if (uint32_t ModID = Writer.getLocalOrImportedSubmoduleID(M)) {
>> +          uint32_t Value = (ModID << 2) | (unsigned)Role;
>>            assert((Value >> 2) == ModID && "overflow in header module
>> info");
>>            LE.write<uint32_t>(Value);
>>          }
>> -      }
>> +      };
>> +
>> +      // FIXME: If the header is excluded, we should write out some
>> +      // record of that fact.
>> +      for (auto ModInfo : Data.KnownHeaders)
>> +        EmitModule(ModInfo.getModule(), ModInfo.getRole());
>> +      if (Data.Unresolved.getPointer())
>> +        EmitModule(Data.Unresolved.getPointer(),
>> Data.Unresolved.getInt());
>>
>>        assert(Out.tell() - Start == DataLen && "Wrong data length");
>>      }
>> @@ -1968,16 +1980,71 @@ namespace {
>>  ///
>>  /// \param HS The header search structure to save.
>>  void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS) {
>> +  HeaderFileInfoTrait GeneratorTrait(*this);
>> +  llvm::OnDiskChainedHashTableGenerator<HeaderFileInfoTrait> Generator;
>> +  SmallVector<const char *, 4> SavedStrings;
>> +  unsigned NumHeaderSearchEntries = 0;
>> +
>> +  // Find all unresolved headers for the current module. We generally
>> will
>> +  // have resolved them before we get here, but not necessarily: we might
>> be
>> +  // compiling a preprocessed module, where there is no requirement for
>> the
>> +  // original files to exist any more.
>> +  if (WritingModule) {
>> +    llvm::SmallVector<Module *, 16> Worklist(1, WritingModule);
>> +    while (!Worklist.empty()) {
>> +      Module *M = Worklist.pop_back_val();
>> +      if (!M->isAvailable())
>> +        continue;
>> +
>> +      // Map to disk files where possible, to pick up any missing stat
>> +      // information. This also means we don't need to check the
>> unresolved
>> +      // headers list when emitting resolved headers in the first loop
>> below.
>> +      // FIXME: It'd be preferable to avoid doing this if we were given
>> +      // sufficient stat information in the module map.
>> +      HS.getModuleMap().resolveHeaderDirectives(M);
>> +
>> +      // If the file didn't exist, we can still create a module if we
>> were given
>> +      // enough information in the module map.
>> +      for (auto U : M->MissingHeaders) {
>> +        // Check that we were given enough information to build a module
>> +        // without this file existing on disk.
>> +        if (!U.Size || (!U.ModTime && IncludeTimestamps)) {
>> +          PP->Diag(U.FileNameLoc,
>> diag::err_module_no_size_mtime_for_header)
>> +            << WritingModule->getFullModuleName() << U.Size.hasValue()
>> +            << U.FileName;
>> +          continue;
>> +        }
>> +
>> +        // Form the effective relative pathname for the file.
>> +        SmallString<128> Filename(M->Directory->getName());
>> +        llvm::sys::path::append(Filename, U.FileName);
>> +        PreparePathForOutput(Filename);
>> +
>> +        StringRef FilenameDup = strdup(Filename.c_str());
>> +        SavedStrings.push_back(FilenameDup.data());
>> +
>> +        HeaderFileInfoTrait::key_type Key = {
>> +          FilenameDup, *U.Size, IncludeTimestamps ? *U.ModTime : 0
>> +        };
>> +        HeaderFileInfoTrait::data_type Data = {
>> +          {}, {}, {M, ModuleMap::headerKindToRole(U.Kind)}
>> +        };
>> +        // FIXME: Deal with cases where there are multiple unresolved
>> header
>> +        // directives in different submodules for the same header.
>> +        Generator.insert(Key, Data, GeneratorTrait);
>> +        ++NumHeaderSearchEntries;
>> +      }
>> +
>> +      Worklist.append(M->submodule_begin(), M->submodule_end());
>> +    }
>> +  }
>> +
>>    SmallVector<const FileEntry *, 16> FilesByUID;
>>    HS.getFileMgr().GetUniqueIDMapping(FilesByUID);
>>
>>    if (FilesByUID.size() > HS.header_file_size())
>>      FilesByUID.resize(HS.header_file_size());
>> -
>> -  HeaderFileInfoTrait GeneratorTrait(*this, HS);
>> -  llvm::OnDiskChainedHashTableGenerator<HeaderFileInfoTrait> Generator;
>> -  SmallVector<const char *, 4> SavedStrings;
>> -  unsigned NumHeaderSearchEntries = 0;
>> +
>>    for (unsigned UID = 0, LastUID = FilesByUID.size(); UID != LastUID;
>> ++UID) {
>>      const FileEntry *File = FilesByUID[UID];
>>      if (!File)
>> @@ -2004,11 +2071,16 @@ void ASTWriter::WriteHeaderSearch(const
>>        SavedStrings.push_back(Filename.data());
>>      }
>>
>> -    HeaderFileInfoTrait::key_type key = { File, Filename };
>> -    Generator.insert(key, *HFI, GeneratorTrait);
>> +    HeaderFileInfoTrait::key_type Key = {
>> +      Filename, File->getSize(), getTimestampForOutput(File)
>> +    };
>> +    HeaderFileInfoTrait::data_type Data = {
>> +      *HFI, HS.getModuleMap().findAllModulesForHeader(File), {}
>> +    };
>> +    Generator.insert(Key, Data, GeneratorTrait);
>>      ++NumHeaderSearchEntries;
>>    }
>> -
>> +
>>    // Create the on-disk hash table in a buffer.
>>    SmallString<4096> TableData;
>>    uint32_t BucketOffset;
>>
>> Added: cfe/trunk/test/Modules/Inputs/header-attribs/bar.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/header-attribs/bar.h?rev=304515&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/test/Modules/Inputs/header-attribs/bar.h (added)
>> +++ cfe/trunk/test/Modules/Inputs/header-attribs/bar.h Thu Jun  1 20:55:39
>> 2017
>> @@ -0,0 +1 @@
>> +extern int b;
>>
>> Added: cfe/trunk/test/Modules/Inputs/header-attribs/baz.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/header-attribs/baz.h?rev=304515&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/test/Modules/Inputs/header-attribs/baz.h (added)
>> +++ cfe/trunk/test/Modules/Inputs/header-attribs/baz.h Thu Jun  1 20:55:39
>> 2017
>> @@ -0,0 +1 @@
>> +extern int c;
>>
>> Added: cfe/trunk/test/Modules/Inputs/header-attribs/foo.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/header-attribs/foo.h?rev=304515&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/test/Modules/Inputs/header-attribs/foo.h (added)
>> +++ cfe/trunk/test/Modules/Inputs/header-attribs/foo.h Thu Jun  1 20:55:39
>> 2017
>> @@ -0,0 +1 @@
>> +extern int a;
>> \ No newline at end of file
>>
>> Added: cfe/trunk/test/Modules/Inputs/header-attribs/modular.modulemap
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/header-attribs/modular.modulemap?rev=304515&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/test/Modules/Inputs/header-attribs/modular.modulemap (added)
>> +++ cfe/trunk/test/Modules/Inputs/header-attribs/modular.modulemap Thu Jun
>> 1 20:55:39 2017
>> @@ -0,0 +1,5 @@
>> +module A {
>> +  header "foo.h" { size 13 }
>> +  header "bar.h" { size 1000 }
>> +  header "baz.h" { mtime 1 }
>> +}
>>
>> Added: cfe/trunk/test/Modules/Inputs/header-attribs/textual.modulemap
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/header-attribs/textual.modulemap?rev=304515&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/test/Modules/Inputs/header-attribs/textual.modulemap (added)
>> +++ cfe/trunk/test/Modules/Inputs/header-attribs/textual.modulemap Thu Jun
>> 1 20:55:39 2017
>> @@ -0,0 +1,5 @@
>> +module A {
>> +  textual header "foo.h" { size 13 }
>> +  textual header "bar.h" { size 1000 }
>> +  textual header "baz.h" { mtime 1 }
>> +}
>>
>> Modified: cfe/trunk/test/Modules/diagnostics.modulemap
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/diagnostics.modulemap?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/test/Modules/diagnostics.modulemap (original)
>> +++ cfe/trunk/test/Modules/diagnostics.modulemap Thu Jun  1 20:55:39 2017
>> @@ -1,4 +1,4 @@
>> -// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t
>> -fmodule-map-file=%S/Inputs/diagnostics-aux.modulemap -fmodule-map-file=%s
>> -fsyntax-only -x c++ /dev/null 2>&1 | FileCheck %s
>> +// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t
>> -fmodule-map-file=%S/Inputs/diagnostics-aux.modulemap -fmodule-map-file=%s
>> -fsyntax-only -x c++ /dev/null 2>&1 | FileCheck %s --implicit-check-not
>> error:
>>
>>  // CHECK: In file included from {{.*}}diagnostics-aux.modulemap:3:
>>  // CHECK: diagnostics-aux-2.modulemap:2:3: error: expected
>> @@ -15,3 +15,15 @@ module bad_use {
>>    // CHECK: diagnostics.modulemap:[[@LINE+1]]:22: error: use declarations
>> are only allowed in top-level modules
>>    module submodule { use foo }
>>  }
>> +
>> +module header_attr {
>> +  // CHECK: diagnostics.modulemap:[[@LINE+1]]:20: error: expected a
>> header attribute name
>> +  header "foo.h" { x }
>> +  // CHECK: diagnostics.modulemap:[[@LINE+1]]:27: error: header attribute
>> 'size' specified multiple times
>> +  header "bar.h" { size 1 size 2 }
>> +  // CHECK: diagnostics.modulemap:[[@LINE+1]]:25: error: expected integer
>> literal as value for header attribute 'size'
>> +  header "baz.h" { size "30 kilobytes" }
>> +
>> +  header "quux.h" { size 1 mtime 2 }
>> +  header "no_attrs.h" {}
>> +}
>>
>> Added: cfe/trunk/test/Modules/header-attribs.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/header-attribs.cpp?rev=304515&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/test/Modules/header-attribs.cpp (added)
>> +++ cfe/trunk/test/Modules/header-attribs.cpp Thu Jun  1 20:55:39 2017
>> @@ -0,0 +1,10 @@
>> +// RUN: rm -rf %t
>> +// RUN: %clang_cc1 -fmodules -I%S/Inputs/header-attribs
>> -fmodule-map-file=%S/Inputs/header-attribs/textual.modulemap
>> -fmodules-cache-path=%t -verify %s -fmodule-name=A -fmodules-strict-decluse
>> +// RUN: not %clang_cc1 -fmodules -I%S/Inputs/header-attribs -emit-module
>> -x c++-module-map %S/Inputs/header-attribs/modular.modulemap
>> -fmodules-cache-path=%t -fmodule-name=A 2>&1 | FileCheck %s --check-prefix
>> BUILD-MODULAR
>> +
>> +#include "foo.h" // ok, stats match
>> +#include "bar.h" // expected-error {{does not depend on a module
>> exporting 'bar.h'}}
>> +#include "baz.h" // expected-error {{does not depend on a module
>> exporting 'baz.h'}}
>> +
>> +// FIXME: Explain why the 'bar.h' found on disk doesn't match the module
>> map.
>> +// BUILD-MODULAR: error: header 'bar.h' not found
>>
>> Added: cfe/trunk/test/Modules/preprocess-missing.modulemap
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/preprocess-missing.modulemap?rev=304515&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/test/Modules/preprocess-missing.modulemap (added)
>> +++ cfe/trunk/test/Modules/preprocess-missing.modulemap Thu Jun  1
>> 20:55:39 2017
>> @@ -0,0 +1,7 @@
>> +// RUN: %clang_cc1 -fmodules -fmodule-name=A -x c++-module-map %s
>> -emit-module -o /dev/null -verify
>> +module A {
>> +  header "does not exist" { size 12345 } // ok, do not need mtime for
>> explicit module build
>> +  header "also does not exist" { mtime 12345 }
>> +}
>> +#pragma clang module contents
>> +// expected-error at 4 {{cannot emit module A: size must be explicitly
>> specified for missing header file "also does not exist"}}
>>
>> Modified: cfe/trunk/test/Modules/preprocess-module.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/preprocess-module.cpp?rev=304515&r1=304514&r2=304515&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/test/Modules/preprocess-module.cpp (original)
>> +++ cfe/trunk/test/Modules/preprocess-module.cpp Thu Jun  1 20:55:39 2017
>> @@ -28,12 +28,21 @@
>>  // RUN: %clang_cc1 -fmodules -fmodule-file=%t/no-rewrite.pcm %s -I%t
>> -verify -fno-modules-error-recovery -DINCLUDE -I%S/Inputs/preprocess
>>  // RUN: %clang_cc1 -fmodules -fmodule-file=%t/rewrite.pcm %s -I%t -verify
>> -fno-modules-error-recovery -DREWRITE -DINCLUDE -I%S/Inputs/preprocess
>>
>> +// Now try building the module when the header files are missing.
>> +// RUN: cp %S/Inputs/preprocess/fwd.h %S/Inputs/preprocess/file.h
>> %S/Inputs/preprocess/file2.h %S/Inputs/preprocess/module.modulemap %t
>> +// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm
>> -I%t -x c++-module-map %t/module.modulemap -E -frewrite-includes -o
>> %t/copy.ii
>> +// RUN: rm %t/fwd.h %t/file.h %t/file2.h %t/module.modulemap
>> +// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm
>> -x c++-module-map-cpp-output %t/copy.ii -emit-module -o %t/copy.pcm
>> +
>> +// Finally, check that our module contains correct mapping information
>> for the headers.
>> +// RUN: cp %S/Inputs/preprocess/fwd.h %S/Inputs/preprocess/file.h
>> %S/Inputs/preprocess/file2.h %S/Inputs/preprocess/module.modulemap %t
>> +// RUN: %clang_cc1 -fmodules -fmodule-file=%t/copy.pcm %s -I%t -verify
>> -fno-modules-error-recovery -DCOPY -DINCLUDE
>>
>>  // == module map
>>  // CHECK: # 1 "{{.*}}module.modulemap"
>>  // CHECK: module file {
>> -// CHECK:   header "file.h"
>> -// CHECK:   header "file2.h"
>> +// CHECK:   header "file.h" { size
>> +// CHECK:   header "file2.h" { size
>>  // CHECK: }
>>
>>  // == file.h
>> @@ -98,6 +107,8 @@
>>  __FILE *a; // expected-error {{declaration of '__FILE' must be imported}}
>>  #ifdef REWRITE
>>  // expected-note at rewrite.ii:1 {{here}}
>> +#elif COPY
>> +// expected-note at copy.ii:1 {{here}}
>>  #else
>>  // expected-note at no-rewrite.ii:1 {{here}}
>>  #endif
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>


More information about the cfe-commits mailing list