jansvoboda11 updated this revision to Diff 386139.
jansvoboda11 added a comment.

Store only direct includes in the PCM (compared to transitive stored 
previously), use InputFile ID (instead of full filesystem path).

Also: add test of transitive includes, fix bug in 
`VisibleModuleSet::setVisible`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D112915/new/

https://reviews.llvm.org/D112915

Files:
  clang/include/clang/Lex/ExternalPreprocessorSource.h
  clang/include/clang/Lex/HeaderSearch.h
  clang/include/clang/Lex/Preprocessor.h
  clang/include/clang/Serialization/ASTBitCodes.h
  clang/include/clang/Serialization/ASTReader.h
  clang/include/clang/Serialization/ASTWriter.h
  clang/include/clang/Serialization/ModuleFile.h
  clang/lib/Basic/Module.cpp
  clang/lib/Lex/HeaderSearch.cpp
  clang/lib/Lex/PPDirectives.cpp
  clang/lib/Lex/PPLexerChange.cpp
  clang/lib/Lex/Preprocessor.cpp
  clang/lib/Serialization/ASTReader.cpp
  clang/lib/Serialization/ASTWriter.cpp
  clang/test/Modules/import-submodule-visibility.c

Index: clang/test/Modules/import-submodule-visibility.c
===================================================================
--- /dev/null
+++ clang/test/Modules/import-submodule-visibility.c
@@ -0,0 +1,99 @@
+// This test checks that imports of headers that appeared in a different submodule than
+// what is imported by the current TU don't affect the compilation.
+
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+//--- A.framework/Headers/A.h
+#include "Textual.h"
+//--- A.framework/Modules/module.modulemap
+framework module A { header "A.h" }
+
+//--- B.framework/Headers/B1.h
+#include "Textual.h"
+//--- B.framework/Headers/B2.h
+//--- B.framework/Modules/module.modulemap
+framework module B {
+  module B1 { header "B1.h" }
+  module B2 { header "B2.h" }
+}
+
+//--- C/C.h
+#include "Textual.h"
+//--- C/module.modulemap
+module C { header "C.h" }
+
+//--- D/D1.h
+#include "Textual.h"
+//--- D/D2.h
+//--- D/module.modulemap
+module D {
+  module D1 { header "D1.h" }
+  module D2 { header "D2.h" }
+}
+
+//--- E/E1.h
+#include "E2.h"
+//--- E/E2.h
+#include "Textual.h"
+//--- E/module.modulemap
+module E {
+  module E1 { header "E1.h" }
+  module E2 { header "E2.h" }
+}
+
+//--- Textual.h
+#define MACRO_TEXTUAL 1
+
+//--- test.c
+
+#ifdef A
+//
+#endif
+
+#ifdef B
+#import <B/B2.h>
+#endif
+
+#ifdef C
+//
+#endif
+
+#ifdef D
+#import "D/D2.h"
+#endif
+
+#ifdef E
+#import "E/E1.h"
+#endif
+
+#import "Textual.h"
+
+static int x = MACRO_TEXTUAL;
+
+// Specifying the PCM file on the command line (without actually importing "A") should not
+// prevent "Textual.h" to be included in the TU.
+//
+// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/A.framework/Modules/module.modulemap -fmodule-name=A -o %t/A.pcm
+// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DA -fmodule-file=%t/A.pcm
+
+// Specifying the PCM file on the command line and importing "B2" in the source does not
+// prevent "Textual.h" to be included in the TU.
+//
+// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/B.framework/Modules/module.modulemap -fmodule-name=B -o %t/B.pcm
+// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DB -iframework %t -fmodule-file=%t/B.pcm
+
+// Module-only version of the test with framework A.
+//
+// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/C/module.modulemap -fmodule-name=C -o %t/C.pcm
+// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DC -fmodule-file=%t/C.pcm
+
+// Module-only version of the test with framework B.
+//
+// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/D/module.modulemap -fmodule-name=D -o %t/D.pcm
+// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DD -fmodule-file=%t/D.pcm
+
+// Transitively imported, but not exported.
+//
+// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/E/module.modulemap -fmodule-name=E -o %t/E.pcm
+// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DE -fmodule-file=%t/E.pcm
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -1696,7 +1696,7 @@
     std::pair<unsigned, unsigned>
     EmitKeyDataLength(raw_ostream& Out, key_type_ref key, data_type_ref Data) {
       unsigned KeyLen = key.Filename.size() + 1 + 8 + 8;
-      unsigned DataLen = 1 + 2 + 4 + 4;
+      unsigned DataLen = 1 + 4 + 4;
       for (auto ModInfo : Data.KnownHeaders)
         if (Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule()))
           DataLen += 4;
@@ -1728,7 +1728,6 @@
                           | (Data.HFI.DirInfo << 1)
                           | Data.HFI.IndexHeaderMapHeader;
       LE.write<uint8_t>(Flags);
-      LE.write<uint16_t>(Data.HFI.NumIncludes);
 
       if (!Data.HFI.ControllingMacro)
         LE.write<uint32_t>(Data.HFI.ControllingMacroID);
@@ -2171,6 +2170,42 @@
   return false;
 }
 
+void ASTWriter::addIncludedFiles(
+    const llvm::DenseMap<const FileEntry *, unsigned> &Files,
+    RecordDataImpl &Record) {
+  // Map the FileEntry map into an input file ID vector.
+  llvm::SmallVector<std::pair<uint64_t, unsigned>> FileIDs;
+  for (const auto &E : Files) {
+    auto InputFileIt = InputFileIDs.find(E.first);
+    assert(InputFileIt != InputFileIDs.end() &&
+           "Included file is an known input file");
+    FileIDs.emplace_back(InputFileIt->second, E.second);
+  }
+
+  // Ensure the order is stable.
+  llvm::sort(FileIDs, [](const auto &LHS, const auto &RHS) {
+    return LHS.first < RHS.first;
+  });
+
+  // [begin, It) - included exactly once
+  // [It, end) - included more than once
+  auto It = std::stable_partition(FileIDs.begin(), FileIDs.end(),
+                                  [](const auto &E) { return E.second == 1; });
+
+  unsigned TotalCount = FileIDs.size();
+  unsigned ExactlyOnceCount = std::distance(FileIDs.begin(), It);
+
+  Record.push_back(TotalCount);
+  // Encode the partition point into the record header. This way, entries can
+  // only contain reference to the file, omitting the include count information.
+  Record.push_back(ExactlyOnceCount);
+
+  for (const auto &E : FileIDs) {
+    assert(E.second != 0 && "Serializing only actually included files");
+    Record.push_back(E.first);
+  }
+}
+
 /// Writes the block containing the serialized form of the
 /// preprocessor.
 void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) {
@@ -2379,6 +2414,10 @@
                                        MacroOffsetsBase - ASTBlockStartOffset};
     Stream.EmitRecordWithBlob(MacroOffsetAbbrev, Record, bytes(MacroOffsets));
   }
+
+  addIncludedFiles(PP.getNullSubmoduleIncludes(), Record);
+  Stream.EmitRecord(PP_NUM_INCLUDES, Record);
+  Record.clear();
 }
 
 void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec,
@@ -2775,6 +2814,13 @@
       Stream.EmitRecordWithBlob(ExportAsAbbrev, Record, Mod->ExportAsModule);
     }
 
+    if (const auto *Includes = PP->getLocalSubmoduleIncludes(Mod)) {
+      RecordData Record;
+      addIncludedFiles(*Includes, Record);
+      Stream.EmitRecord(SUBMODULE_NUM_INCLUDES, Record);
+      Record.clear();
+    }
+
     // Queue up the submodules of this module.
     for (auto *M : Mod->submodules())
       Q.push(M);
Index: clang/lib/Serialization/ASTReader.cpp
===================================================================
--- clang/lib/Serialization/ASTReader.cpp
+++ clang/lib/Serialization/ASTReader.cpp
@@ -1888,10 +1888,6 @@
   HFI.isPragmaOnce |= (Flags >> 4) & 0x01;
   HFI.DirInfo = (Flags >> 1) & 0x07;
   HFI.IndexHeaderMapHeader = Flags & 0x01;
-  // FIXME: Find a better way to handle this. Maybe just store a
-  // "has been included" flag?
-  HFI.NumIncludes = std::max(endian::readNext<uint16_t, little, unaligned>(d),
-                             HFI.NumIncludes);
   HFI.ControllingMacroID = Reader.getGlobalIdentifierID(
       M, endian::readNext<uint32_t, little, unaligned>(d));
   if (unsigned FrameworkOffset =
@@ -3701,6 +3697,15 @@
       break;
     }
 
+    case PP_NUM_INCLUDES: {
+      if (F.isModule())
+        break;
+
+      for (const auto &E : readIncludedFiles(F, Record))
+        PP.incrementTransitiveIncludeCount(E.getFirst(), E.getSecond());
+      break;
+    }
+
     case LATE_PARSED_TEMPLATE:
       LateParsedTemplates.emplace_back(
           std::piecewise_construct, std::forward_as_tuple(&F),
@@ -5711,6 +5716,10 @@
       CurrentModule->ExportAsModule = Blob.str();
       ModMap.addLinkAsDependency(CurrentModule);
       break;
+
+    case SUBMODULE_NUM_INCLUDES:
+      F.SubmoduleIncludedFiles.insert(
+          {CurrentModule->getFullModuleName(), Record});
     }
   }
 }
@@ -8594,6 +8603,49 @@
   return LocalID + I->second;
 }
 
+llvm::DenseMap<const FileEntry *, unsigned>
+ASTReader::readIncludedFiles(ModuleFile &F, RecordData &Record) {
+  unsigned Idx = 0;
+  unsigned TotalCount = Record[Idx++];
+  unsigned IncludedOnceCount = Record[Idx++];
+
+  auto ReadFile = [&]() -> OptionalFileEntryRefDegradesToFileEntryPtr {
+    unsigned InputFileID = Record[Idx++];
+    auto InputFileInfo = readInputFileInfo(F, InputFileID);
+    return FileMgr.getOptionalFileRef(InputFileInfo.Filename);
+  };
+
+  llvm::DenseMap<const FileEntry *, unsigned> Result;
+  for (unsigned I = 0; I < IncludedOnceCount; ++I)
+    if (const FileEntry *File = ReadFile())
+      Result.insert({File, 1});
+  for (unsigned I = 0; I < TotalCount - IncludedOnceCount; ++I)
+    if (const FileEntry *File = ReadFile())
+      Result.insert({File, 2}); // squash "more than one" include into exactly 2
+  return Result;
+}
+
+const llvm::DenseMap<const FileEntry *, unsigned> *
+ASTReader::getIncludedFiles(Module *M) {
+  ModuleFile *F = getModuleManager().lookup(M->getASTFile());
+  if (!F)
+    return nullptr;
+
+  auto ResultIt = SubmoduleIncludedFiles.insert(
+      {M, llvm::DenseMap<const FileEntry *, unsigned>{}});
+  auto &Result = ResultIt.first->second;
+  if (!ResultIt.second)
+    return &Result;
+
+  auto It = F->SubmoduleIncludedFiles.find(M->getFullModuleName());
+  if (It == F->SubmoduleIncludedFiles.end())
+    return nullptr;
+  auto &Record = It->second;
+
+  Result = readIncludedFiles(*F, Record);
+  return &Result;
+}
+
 Module *ASTReader::getSubmodule(SubmoduleID GlobalID) {
   if (GlobalID < NUM_PREDEF_SUBMODULE_IDS) {
     assert(GlobalID == 0 && "Unhandled global submodule ID");
Index: clang/lib/Lex/Preprocessor.cpp
===================================================================
--- clang/lib/Lex/Preprocessor.cpp
+++ clang/lib/Lex/Preprocessor.cpp
@@ -91,7 +91,8 @@
       // deferred to Preprocessor::Initialize().
       Identifiers(IILookup), PragmaHandlers(new PragmaNamespace(StringRef())),
       TUKind(TUKind), SkipMainFilePreamble(0, true),
-      CurSubmoduleState(&NullSubmoduleState) {
+      CurSubmoduleState(&NullSubmoduleState),
+      CurSubmoduleIncludeState(&NullSubmoduleIncludeState) {
   OwnsHeaderSearch = OwnsHeaders;
 
   // Default to discarding comments.
@@ -286,6 +287,15 @@
              << " token paste (##) operations performed, "
              << NumFastTokenPaste << " on the fast path.\n";
 
+  unsigned NumSingleIncludedFiles = 0, MaxNumIncludes = 0;
+  for (const auto &Include : IncludedFiles) {
+    if (MaxNumIncludes < Include.second)
+      MaxNumIncludes = Include.second;
+    NumSingleIncludedFiles += Include.second == 1;
+  }
+  llvm::errs() << NumSingleIncludedFiles << " files included exactly once.\n"
+               << MaxNumIncludes << " max times a file is included.\n";
+
   llvm::errs() << "\nPreprocessor Memory: " << getTotalMemory() << "B total";
 
   llvm::errs() << "\n  BumpPtr: " << BP.getTotalMemory();
@@ -548,10 +558,15 @@
 
     // Tell the header info that the main file was entered.  If the file is later
     // #imported, it won't be re-entered.
-    if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID))
-      HeaderInfo.IncrementIncludeCount(FE);
+    if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID)) {
+      (void)getHeaderSearchInfo().getFileInfo(FE);
+      incrementIncludeCount(FE);
+    }
   }
 
+  if (Module *M = getCurrentModule())
+    CurSubmoduleIncludeState = &SubmoduleIncludeStates[M];
+
   // Preprocess Predefines to populate the initial preprocessor state.
   std::unique_ptr<llvm::MemoryBuffer> SB =
     llvm::MemoryBuffer::getMemBufferCopy(Predefines, "<built-in>");
@@ -1315,6 +1330,18 @@
             << Message;
       });
 
+  CurSubmoduleIncludeState->VisibleModules.setVisible(
+      M, Loc,
+      [&](Module *M) {
+        const auto *Includes = getLocalSubmoduleIncludes(M);
+        if (!Includes)
+          Includes = getExternalSubmoduleIncludes(M);
+        if (Includes)
+          for (const auto &E : *Includes)
+            incrementTransitiveIncludeCount(E.getFirst(), E.getSecond());
+      },
+      [](ArrayRef<Module *>, Module *, StringRef) {});
+
   // Add this module to the imports list of the currently-built submodule.
   if (!BuildingSubmoduleStack.empty() && M != BuildingSubmoduleStack.back().M)
     BuildingSubmoduleStack.back().M->Imports.insert(M);
@@ -1466,3 +1493,45 @@
   Record = new PreprocessingRecord(getSourceManager());
   addPPCallbacks(std::unique_ptr<PPCallbacks>(Record));
 }
+
+bool Preprocessor::includedAtLeastOnce(const FileEntry *File) const {
+  auto It = IncludedFiles.find(File);
+  if (It == IncludedFiles.end())
+    return false;
+  return It->second >= 1;
+}
+
+bool Preprocessor::firstTimeLexingFile(const FileEntry *File) const {
+  auto It = IncludedFiles.find(File);
+  if (It == IncludedFiles.end())
+    return false;
+  return It->second == 1;
+}
+
+void Preprocessor::incrementTransitiveIncludeCount(const FileEntry *File,
+                                                   unsigned Count) {
+  // Compared to `incrementIncludeCount`, we're not incrementing the count in
+  // CurSubmoduleIncludeState`. This is to ensure it always only contains
+  // **locally** included files and not transitively included ones.
+  IncludedFiles[File] += Count;
+}
+
+void Preprocessor::incrementIncludeCount(const FileEntry *File) {
+  CurSubmoduleIncludeState->IncludedFiles[File] += 1;
+  IncludedFiles[File] += 1;
+}
+
+const llvm::DenseMap<const FileEntry *, unsigned> *
+Preprocessor::getLocalSubmoduleIncludes(Module *M) const {
+  auto It = SubmoduleIncludeStates.find(M);
+  if (It != SubmoduleIncludeStates.end())
+    return &It->second.IncludedFiles;
+  return nullptr;
+}
+
+const llvm::DenseMap<const FileEntry *, unsigned> *
+Preprocessor::getExternalSubmoduleIncludes(Module *M) const {
+  if (ExternalSource)
+    return ExternalSource->getIncludedFiles(M);
+  return nullptr;
+}
Index: clang/lib/Lex/PPLexerChange.cpp
===================================================================
--- clang/lib/Lex/PPLexerChange.cpp
+++ clang/lib/Lex/PPLexerChange.cpp
@@ -376,8 +376,7 @@
         if (const IdentifierInfo *DefinedMacro =
               CurPPLexer->MIOpt.GetDefinedMacro()) {
           if (!isMacroDefined(ControllingMacro) &&
-              DefinedMacro != ControllingMacro &&
-              HeaderInfo.FirstTimeLexingFile(FE)) {
+              DefinedMacro != ControllingMacro && firstTimeLexingFile(FE)) {
 
             // If the edit distance between the two macros is more than 50%,
             // DefinedMacro may not be header guard, or can be header guard of
@@ -688,9 +687,12 @@
                                   bool ForPragma) {
   if (!getLangOpts().ModulesLocalVisibility) {
     // Just track that we entered this submodule.
-    BuildingSubmoduleStack.push_back(
-        BuildingSubmoduleInfo(M, ImportLoc, ForPragma, CurSubmoduleState,
-                              PendingModuleMacroNames.size()));
+    BuildingSubmoduleStack.push_back(BuildingSubmoduleInfo(
+        M, ImportLoc, ForPragma, CurSubmoduleState,
+        PendingModuleMacroNames.size(), CurSubmoduleIncludeState));
+
+    CurSubmoduleIncludeState = &SubmoduleIncludeStates[M];
+
     if (Callbacks)
       Callbacks->EnteredSubmodule(M, ImportLoc, ForPragma);
     return;
@@ -733,15 +735,16 @@
   }
 
   // Track that we entered this module.
-  BuildingSubmoduleStack.push_back(
-      BuildingSubmoduleInfo(M, ImportLoc, ForPragma, CurSubmoduleState,
-                            PendingModuleMacroNames.size()));
+  BuildingSubmoduleStack.push_back(BuildingSubmoduleInfo(
+      M, ImportLoc, ForPragma, CurSubmoduleState,
+      PendingModuleMacroNames.size(), CurSubmoduleIncludeState));
 
   if (Callbacks)
     Callbacks->EnteredSubmodule(M, ImportLoc, ForPragma);
 
   // Switch to this submodule as the current submodule.
   CurSubmoduleState = &State;
+  CurSubmoduleIncludeState = &SubmoduleIncludeStates[M];
 
   // This module is visible to itself.
   if (FirstTime)
@@ -773,6 +776,8 @@
   Module *LeavingMod = Info.M;
   SourceLocation ImportLoc = Info.ImportLoc;
 
+  CurSubmoduleIncludeState = Info.OuterSubmoduleIncludeState;
+
   if (!needModuleMacros() ||
       (!getLangOpts().ModulesLocalVisibility &&
        LeavingMod->getTopLevelModuleName() != getLangOpts().CurrentModule)) {
Index: clang/lib/Lex/PPDirectives.cpp
===================================================================
--- clang/lib/Lex/PPDirectives.cpp
+++ clang/lib/Lex/PPDirectives.cpp
@@ -2051,7 +2051,7 @@
   // include cycle. Don't enter already processed files again as it can lead to
   // reaching the max allowed include depth again.
   if (Action == Enter && HasReachedMaxIncludeDepth && File &&
-      HeaderInfo.getFileInfo(&File->getFileEntry()).NumIncludes)
+      includedAtLeastOnce(*File))
     Action = IncludeLimitReached;
 
   // Determine whether we should try to import the module for this #include, if
Index: clang/lib/Lex/HeaderSearch.cpp
===================================================================
--- clang/lib/Lex/HeaderSearch.cpp
+++ clang/lib/Lex/HeaderSearch.cpp
@@ -89,16 +89,10 @@
 void HeaderSearch::PrintStats() {
   llvm::errs() << "\n*** HeaderSearch Stats:\n"
                << FileInfo.size() << " files tracked.\n";
-  unsigned NumOnceOnlyFiles = 0, MaxNumIncludes = 0, NumSingleIncludedFiles = 0;
-  for (unsigned i = 0, e = FileInfo.size(); i != e; ++i) {
+  unsigned NumOnceOnlyFiles = 0;
+  for (unsigned i = 0, e = FileInfo.size(); i != e; ++i)
     NumOnceOnlyFiles += (FileInfo[i].isPragmaOnce || FileInfo[i].isImport);
-    if (MaxNumIncludes < FileInfo[i].NumIncludes)
-      MaxNumIncludes = FileInfo[i].NumIncludes;
-    NumSingleIncludedFiles += FileInfo[i].NumIncludes == 1;
-  }
-  llvm::errs() << "  " << NumOnceOnlyFiles << " #import/#pragma once files.\n"
-               << "  " << NumSingleIncludedFiles << " included exactly once.\n"
-               << "  " << MaxNumIncludes << " max times a file is included.\n";
+  llvm::errs() << "  " << NumOnceOnlyFiles << " #import/#pragma once files.\n";
 
   llvm::errs() << "  " << NumIncluded << " #include/#include_next/#import.\n"
                << "    " << NumMultiIncludeFileOptzn
@@ -1203,7 +1197,6 @@
   HFI.isImport |= OtherHFI.isImport;
   HFI.isPragmaOnce |= OtherHFI.isPragmaOnce;
   HFI.isModuleHeader |= OtherHFI.isModuleHeader;
-  HFI.NumIncludes += OtherHFI.NumIncludes;
 
   if (!HFI.ControllingMacro && !HFI.ControllingMacroID) {
     HFI.ControllingMacro = OtherHFI.ControllingMacro;
@@ -1361,7 +1354,7 @@
     FileInfo.isImport = true;
 
     // Has this already been #import'ed or #include'd?
-    if (FileInfo.NumIncludes && !TryEnterImported())
+    if (PP.includedAtLeastOnce(File) && !TryEnterImported())
       return false;
   } else {
     // Otherwise, if this is a #include of a file that was previously #import'd
@@ -1384,8 +1377,7 @@
     }
   }
 
-  // Increment the number of times this file has been included.
-  ++FileInfo.NumIncludes;
+  PP.incrementIncludeCount(File);
 
   return true;
 }
Index: clang/lib/Basic/Module.cpp
===================================================================
--- clang/lib/Basic/Module.cpp
+++ clang/lib/Basic/Module.cpp
@@ -650,7 +650,7 @@
       return;
 
     ImportLocs[ID] = Loc;
-    Vis(M);
+    Vis(V.M);
 
     // Make any exported modules visible.
     SmallVector<Module *, 16> Exports;
Index: clang/include/clang/Serialization/ModuleFile.h
===================================================================
--- clang/include/clang/Serialization/ModuleFile.h
+++ clang/include/clang/Serialization/ModuleFile.h
@@ -394,6 +394,10 @@
   /// Remapping table for submodule IDs in this module.
   ContinuousRangeMap<uint32_t, int, 2> SubmoduleRemap;
 
+  /// Mapping between (sub)module names and the serialized version of file
+  /// include information.
+  llvm::StringMap<llvm::SmallVector<uint64_t, 64>> SubmoduleIncludedFiles;
+
   // === Selectors ===
 
   /// The number of selectors new to this file.
Index: clang/include/clang/Serialization/ASTWriter.h
===================================================================
--- clang/include/clang/Serialization/ASTWriter.h
+++ clang/include/clang/Serialization/ASTWriter.h
@@ -592,6 +592,11 @@
   void AddAlignPackInfo(const Sema::AlignPackInfo &Info,
                         RecordDataImpl &Record);
 
+  /// Emit included files information.
+  void
+  addIncludedFiles(const llvm::DenseMap<const FileEntry *, unsigned> &Files,
+                   RecordDataImpl &Record);
+
   /// Emit a source location.
   void AddSourceLocation(SourceLocation Loc, RecordDataImpl &Record);
 
Index: clang/include/clang/Serialization/ASTReader.h
===================================================================
--- clang/include/clang/Serialization/ASTReader.h
+++ clang/include/clang/Serialization/ASTReader.h
@@ -927,6 +927,10 @@
   /// A list of modules that were imported by precompiled headers or
   /// any other non-module AST file.
   SmallVector<ImportedSubmodule, 2> ImportedModules;
+
+  /// Mapping between a read (sub)module and the file include information.
+  llvm::DenseMap<Module *, llvm::DenseMap<const FileEntry *, unsigned>>
+      SubmoduleIncludedFiles;
   //@}
 
   /// The system include root to be used when loading the
@@ -1380,6 +1384,10 @@
   RecordLocation getLocalBitOffset(uint64_t GlobalOffset);
   uint64_t getGlobalBitOffset(ModuleFile &M, uint64_t LocalOffset);
 
+  /// Read the record containing files included in a (sub)module.
+  llvm::DenseMap<const FileEntry *, unsigned>
+  readIncludedFiles(ModuleFile &F, RecordData &Record);
+
   /// Returns the first preprocessed entity ID that begins or ends after
   /// \arg Loc.
   serialization::PreprocessedEntityID
@@ -2099,6 +2107,10 @@
   ///
   Module *getSubmodule(serialization::SubmoduleID GlobalID);
 
+  /// Return the files directly included in the given (sub)module.
+  const llvm::DenseMap<const FileEntry *, unsigned> *
+  getIncludedFiles(Module *M) override;
+
   /// Retrieve the module that corresponds to the given module ID.
   ///
   /// Note: overrides method in ExternalASTSource
Index: clang/include/clang/Serialization/ASTBitCodes.h
===================================================================
--- clang/include/clang/Serialization/ASTBitCodes.h
+++ clang/include/clang/Serialization/ASTBitCodes.h
@@ -695,6 +695,9 @@
 
   /// Record code for \#pragma float_control options.
   FLOAT_CONTROL_PRAGMA_OPTIONS = 65,
+
+  /// Record code for includes outside of any (sub)module, used by PCH.
+  PP_NUM_INCLUDES = 66,
 };
 
 /// Record types used within a source manager block.
@@ -825,6 +828,9 @@
   /// Specifies the name of the module that will eventually
   /// re-export the entities in this module.
   SUBMODULE_EXPORT_AS = 17,
+
+  /// Specifies the number of includes of each file.
+  SUBMODULE_NUM_INCLUDES = 18,
 };
 
 /// Record types used within a comments block.
Index: clang/include/clang/Lex/Preprocessor.h
===================================================================
--- clang/include/clang/Lex/Preprocessor.h
+++ clang/include/clang/Lex/Preprocessor.h
@@ -718,6 +718,7 @@
   using MacroMap = llvm::DenseMap<const IdentifierInfo *, MacroState>;
 
   struct SubmoduleState;
+  struct SubmoduleIncludeState;
 
   /// Information about a submodule that we're currently building.
   struct BuildingSubmoduleInfo {
@@ -736,12 +737,17 @@
     /// The number of pending module macro names when we started building this.
     unsigned OuterPendingModuleMacroNames;
 
+    /// The previous SubmoduleIncludeState.
+    SubmoduleIncludeState *OuterSubmoduleIncludeState;
+
     BuildingSubmoduleInfo(Module *M, SourceLocation ImportLoc, bool IsPragma,
                           SubmoduleState *OuterSubmoduleState,
-                          unsigned OuterPendingModuleMacroNames)
+                          unsigned OuterPendingModuleMacroNames,
+                          SubmoduleIncludeState *OuterSubmoduleIncludeState)
         : M(M), ImportLoc(ImportLoc), IsPragma(IsPragma),
           OuterSubmoduleState(OuterSubmoduleState),
-          OuterPendingModuleMacroNames(OuterPendingModuleMacroNames) {}
+          OuterPendingModuleMacroNames(OuterPendingModuleMacroNames),
+          OuterSubmoduleIncludeState(OuterSubmoduleIncludeState) {}
   };
   SmallVector<BuildingSubmoduleInfo, 8> BuildingSubmoduleStack;
 
@@ -765,6 +771,26 @@
   /// in a submodule.
   SubmoduleState *CurSubmoduleState;
 
+  /// Information about a (sub)module's preprocessor include state.
+  struct SubmoduleIncludeState {
+    /// For each included file, we track the number of includes.
+    llvm::DenseMap<const FileEntry *, unsigned> IncludedFiles;
+
+    /// The set of modules that are visible within the submodule.
+    VisibleModuleSet VisibleModules;
+  };
+  /// The include state for each (sub)module.
+  std::map<Module *, SubmoduleIncludeState> SubmoduleIncludeStates;
+
+  /// The include state outside of any (sub)module.
+  SubmoduleIncludeState NullSubmoduleIncludeState;
+
+  /// The current include state.
+  SubmoduleIncludeState *CurSubmoduleIncludeState;
+
+  /// For each included file, we track the number of includes.
+  llvm::DenseMap<const FileEntry *, unsigned> IncludedFiles;
+
   /// The set of known macros exported from modules.
   llvm::FoldingSet<ModuleMacro> ModuleMacros;
 
@@ -920,6 +946,10 @@
 
   void updateOutOfDateIdentifier(IdentifierInfo &II) const;
 
+  /// Get the external include information for the given (sub)module.
+  const llvm::DenseMap<const FileEntry *, unsigned> *
+  getExternalSubmoduleIncludes(Module *M) const;
+
 public:
   Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
                DiagnosticsEngine &diags, LangOptions &opts, SourceManager &SM,
@@ -1224,6 +1254,28 @@
 
   /// \}
 
+  /// Check whether the file has been included at least once.
+  bool includedAtLeastOnce(const FileEntry *File) const;
+
+  /// Check whether this is the first time encountering the header file.
+  bool firstTimeLexingFile(const FileEntry *File) const;
+
+  /// Increment the include counter for a locally included file by one.
+  void incrementIncludeCount(const FileEntry *File);
+
+  /// Increment the include counter for a file transitively included file.
+  void incrementTransitiveIncludeCount(const FileEntry *File, unsigned Count);
+
+  /// Get the include information outside of any (sub)module.
+  const llvm::DenseMap<const FileEntry *, unsigned> &
+  getNullSubmoduleIncludes() const {
+    return NullSubmoduleIncludeState.IncludedFiles;
+  }
+
+  /// Get the include information for the given local (sub)module.
+  const llvm::DenseMap<const FileEntry *, unsigned> *
+  getLocalSubmoduleIncludes(Module *M) const;
+
   /// Return the name of the macro defined before \p Loc that has
   /// spelling \p Tokens.  If there are multiple macros with same spelling,
   /// return the last one defined.
Index: clang/include/clang/Lex/HeaderSearch.h
===================================================================
--- clang/include/clang/Lex/HeaderSearch.h
+++ clang/include/clang/Lex/HeaderSearch.h
@@ -89,9 +89,6 @@
   /// Whether this file has been looked up as a header.
   unsigned IsValid : 1;
 
-  /// The number of times the file has been included already.
-  unsigned short NumIncludes = 0;
-
   /// The ID number of the controlling macro.
   ///
   /// This ID number will be non-zero when there is a controlling
@@ -130,13 +127,6 @@
   /// any.
   const IdentifierInfo *
   getControllingMacro(ExternalPreprocessorSource *External);
-
-  /// Determine whether this is a non-default header file info, e.g.,
-  /// it corresponds to an actual header we've included or tried to include.
-  bool isNonDefault() const {
-    return isImport || isPragmaOnce || NumIncludes || ControllingMacro ||
-      ControllingMacroID;
-  }
 };
 
 /// An external source of header file information, which may supply
@@ -481,12 +471,6 @@
                             ModuleMap::ModuleHeaderRole Role,
                             bool isCompilingModuleHeader);
 
-  /// Increment the count for the number of times the specified
-  /// FileEntry has been entered.
-  void IncrementIncludeCount(const FileEntry *File) {
-    ++getFileInfo(File).NumIncludes;
-  }
-
   /// Mark the specified file as having a controlling macro.
   ///
   /// This is used by the multiple-include optimization to eliminate
@@ -496,11 +480,6 @@
     getFileInfo(File).ControllingMacro = ControllingMacro;
   }
 
-  /// Return true if this is the first time encountering this header.
-  bool FirstTimeLexingFile(const FileEntry *File) {
-    return getFileInfo(File).NumIncludes == 1;
-  }
-
   /// Determine whether this file is intended to be safe from
   /// multiple inclusions, e.g., it has \#pragma once or a controlling
   /// macro.
Index: clang/include/clang/Lex/ExternalPreprocessorSource.h
===================================================================
--- clang/include/clang/Lex/ExternalPreprocessorSource.h
+++ clang/include/clang/Lex/ExternalPreprocessorSource.h
@@ -40,6 +40,10 @@
 
   /// Map a module ID to a module.
   virtual Module *getModule(unsigned ModuleID) = 0;
+
+  /// Return the files directly included in the given (sub)module.
+  virtual const llvm::DenseMap<const FileEntry *, unsigned> *
+  getIncludedFiles(Module *M) = 0;
 };
 
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to