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

Avoid copying data between submodules


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/HeaderSearch.h
  clang/include/clang/Lex/Preprocessor.h
  clang/include/clang/Serialization/ASTBitCodes.h
  clang/include/clang/Serialization/ASTReader.h
  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/Inputs/import-submodule-visibility/A.framework/Headers/A.h
  
clang/test/Modules/Inputs/import-submodule-visibility/A.framework/Modules/module.modulemap
  clang/test/Modules/Inputs/import-submodule-visibility/B.framework/Headers/B1.h
  clang/test/Modules/Inputs/import-submodule-visibility/B.framework/Headers/B2.h
  
clang/test/Modules/Inputs/import-submodule-visibility/B.framework/Modules/module.modulemap
  clang/test/Modules/Inputs/import-submodule-visibility/C/C.h
  clang/test/Modules/Inputs/import-submodule-visibility/C/module.modulemap
  clang/test/Modules/Inputs/import-submodule-visibility/D/D1.h
  clang/test/Modules/Inputs/import-submodule-visibility/D/D2.h
  clang/test/Modules/Inputs/import-submodule-visibility/D/module.modulemap
  clang/test/Modules/Inputs/import-submodule-visibility/Textual.h
  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,58 @@
+// RUN: rm -rf %t && mkdir -p %t
+
+// 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.
+
+// Framework "A" includes "Textual.h" in the top-level module.
+// This test checks that simply specifying the PCM file on the command line (without actually importing "A") does not
+// prevent "Textual.h" to be included in the TU.
+//
+// RUN: %clang_cc1 -fmodules -I %S/Inputs/import-submodule-visibility \
+// RUN:   -emit-module %S/Inputs/import-submodule-visibility/A.framework/Modules/module.modulemap -fmodule-name=A -o %t/A.pcm
+// RUN: %clang_cc1 -fmodules -I %S/Inputs/import-submodule-visibility -fsyntax-only %s -DA \
+// RUN:   -fmodule-file=%t/A.pcm -fmodule-map-file=%S/Inputs/import-submodule-visibility/A.framework/Modules/module.modulemap
+
+// Framework "B" includes "Textual.h" in the "B1" submodule and its "B2" submodule does not include "Textual.h" at all.
+// This test checks that 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 %S/Inputs/import-submodule-visibility \
+// RUN:   -emit-module %S/Inputs/import-submodule-visibility/B.framework/Modules/module.modulemap -fmodule-name=B -o %t/B.pcm
+// RUN: %clang_cc1 -fmodules -I %S/Inputs/import-submodule-visibility -fsyntax-only %s -DB -iframework %S/Inputs/import-submodule-visibility \
+// RUN:   -fmodule-file=%t/B.pcm -fmodule-map-file=%S/Inputs/import-submodule-visibility/B.framework/Modules/module.modulemap
+
+// Module "C" includes "Textual.h" in the top level module.
+// This is a module-only version of the test with framework A.
+//
+// RUN: %clang_cc1 -fmodules -I %S/Inputs/import-submodule-visibility \
+// RUN:   -emit-module %S/Inputs/import-submodule-visibility/C/module.modulemap -fmodule-name=C -o %t/C.pcm
+// RUN: %clang_cc1 -fmodules -I %S/Inputs/import-submodule-visibility -fsyntax-only %s -DC \
+// RUN:   -fmodule-file=%t/C.pcm -fmodule-map-file=%S/Inputs/import-submodule-visibility/C/module.modulemap
+
+// Module "D" includes "Textual.h" in the "D1" submodule and its "D2" submodules does not include "Textual.h" at all.
+// This is a module-only version of the test with framework B.
+//
+// RUN: %clang_cc1 -fmodules -I %S/Inputs/import-submodule-visibility \
+// RUN:   -emit-module %S/Inputs/import-submodule-visibility/D/module.modulemap -fmodule-name=D -o %t/D.pcm
+// RUN: %clang_cc1 -fmodules -I %S/Inputs/import-submodule-visibility -fsyntax-only %s -DD \
+// RUN:   -fmodule-file=%t/D.pcm -fmodule-map-file=%S/Inputs/import-submodule-visibility/D/module.modulemap
+
+#ifdef A
+//
+#endif
+
+#ifdef B
+#import <B/B2.h>
+#endif
+
+#ifdef C
+//
+#endif
+
+#ifdef D
+#import "D/D2.h"
+#endif
+
+#import "Textual.h"
+
+static int x = MACRO_TEXTUAL;
Index: clang/test/Modules/Inputs/import-submodule-visibility/Textual.h
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/import-submodule-visibility/Textual.h
@@ -0,0 +1 @@
+#define MACRO_TEXTUAL 1
Index: clang/test/Modules/Inputs/import-submodule-visibility/D/module.modulemap
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/import-submodule-visibility/D/module.modulemap
@@ -0,0 +1,4 @@
+module D {
+  module D1 { header "D1.h" }
+  module D2 { header "D2.h" }
+}
Index: clang/test/Modules/Inputs/import-submodule-visibility/D/D1.h
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/import-submodule-visibility/D/D1.h
@@ -0,0 +1 @@
+#include "Textual.h"
Index: clang/test/Modules/Inputs/import-submodule-visibility/C/module.modulemap
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/import-submodule-visibility/C/module.modulemap
@@ -0,0 +1 @@
+module C { header "C.h" }
Index: clang/test/Modules/Inputs/import-submodule-visibility/C/C.h
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/import-submodule-visibility/C/C.h
@@ -0,0 +1 @@
+#include "Textual.h"
Index: clang/test/Modules/Inputs/import-submodule-visibility/B.framework/Modules/module.modulemap
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/import-submodule-visibility/B.framework/Modules/module.modulemap
@@ -0,0 +1,4 @@
+framework module B {
+  module B1 { header "B1.h" }
+  module B2 { header "B2.h" }
+}
Index: clang/test/Modules/Inputs/import-submodule-visibility/B.framework/Headers/B1.h
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/import-submodule-visibility/B.framework/Headers/B1.h
@@ -0,0 +1 @@
+#include "Textual.h"
Index: clang/test/Modules/Inputs/import-submodule-visibility/A.framework/Modules/module.modulemap
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/import-submodule-visibility/A.framework/Modules/module.modulemap
@@ -0,0 +1,3 @@
+framework module A {
+  header "A.h"
+}
Index: clang/test/Modules/Inputs/import-submodule-visibility/A.framework/Headers/A.h
===================================================================
--- /dev/null
+++ clang/test/Modules/Inputs/import-submodule-visibility/A.framework/Headers/A.h
@@ -0,0 +1 @@
+#include "Textual.h"
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -116,6 +116,8 @@
 #include <utility>
 #include <vector>
 
+#include <map>
+
 using namespace clang;
 using namespace clang::serialization;
 
@@ -1696,7 +1698,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 +1730,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);
@@ -2379,6 +2380,44 @@
                                        MacroOffsetsBase - ASTBlockStartOffset};
     Stream.EmitRecordWithBlob(MacroOffsetAbbrev, Record, bytes(MacroOffsets));
   }
+
+  Abbrev = std::make_shared<BitCodeAbbrev>();
+  Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_INCLUDES));
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // submodule ID
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // # of includes
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+
+  unsigned IncludesAbbrev = Stream.EmitAbbrev(std::move(Abbrev));
+
+  // TODO: Use plain vector, submodule IDs are small and dense.
+  std::map<unsigned, std::map<StringRef, unsigned>> ToBeSerialized;
+
+  auto CollectModuleIncludes = [&](Module *M, const auto &Includes) {
+    unsigned LocalSMID = getLocalOrImportedSubmoduleID(M);
+
+    for (const auto &Inc : Includes.NumIncludes.getAll(PP.getFileManager())) {
+      unsigned NumIncludes = Inc.second;
+      StringRef Filename = Inc.first->getName();
+      ToBeSerialized[LocalSMID].insert({Filename, NumIncludes});
+    }
+  };
+
+  CollectModuleIncludes(const_cast<Preprocessor &>(PP).getCurrentModule(),
+                        PP.getNullSubmoduleIncludeState());
+  for (const auto &SubM : PP.getSubmoduleIncludeStates())
+    CollectModuleIncludes(SubM.first, SubM.second);
+
+  for (const auto& Include : ToBeSerialized) {
+    unsigned LocalSMID = Include.first;
+
+    for (const auto& X : Include.second) {
+      StringRef Filename = X.first;
+      unsigned NumIncludes = X.second;
+      RecordData::value_type Record[] = {SUBMODULE_INCLUDES, LocalSMID,
+                                         NumIncludes};
+      Stream.EmitRecordWithBlob(IncludesAbbrev, Record, Filename);
+    }
+  }
 }
 
 void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec,
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,19 @@
       break;
     }
 
+    case SUBMODULE_INCLUDES: {
+      unsigned LocalSMID = Record[0];
+      unsigned NumIncludes = Record[1];
+      StringRef Filename = Blob;
+
+      // PCHs don't have submodules. We can add the includes right away.
+      if (PP.getPreprocessorOpts().ImplicitPCHInclude == F.FileName)
+        PP.addSubmoduleInclude(nullptr, Filename, NumIncludes);
+      else
+        SubmoduleImports[LocalSMID][Filename] = NumIncludes;
+      break;
+    }
+
     case LATE_PARSED_TEMPLATE:
       LateParsedTemplates.emplace_back(
           std::piecewise_construct, std::forward_as_tuple(&F),
@@ -5569,6 +5578,21 @@
       CurrentModule->IsUnimportable =
           ParentModule && ParentModule->IsUnimportable;
       CurrentModule->IsAvailable = !CurrentModule->IsUnimportable;
+
+      // Now that we can map the local submodule ID to an actual Module
+      // instance, pass the include information to the preprocessor.
+      // TODO: Avoid iteration here.
+      for (const auto &Submodule : SubmoduleImports) {
+        unsigned LocalID = Submodule.getFirst();
+        if (GlobalID == getGlobalSubmoduleID(F, LocalID)) {
+          for (const auto &Include : Submodule.getSecond()) {
+            PP.addSubmoduleInclude(CurrentModule, Include.getKey(),
+                                   Include.getValue());
+          }
+          break;
+        }
+      }
+
       break;
     }
 
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.
@@ -549,7 +550,7 @@
     // 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);
+      IncrementIncludeCount(FE);
   }
 
   // Preprocess Predefines to populate the initial preprocessor state.
@@ -1315,6 +1316,17 @@
             << Message;
       });
 
+  CurSubmoduleIncludeState->VisibleModules.setVisible(
+      M, Loc,
+      [&](Module *M) {
+        auto &ModNumIncludes = SubmoduleIncludeStates[M].NumIncludes;
+        for (unsigned UID = 0; UID <= ModNumIncludes.maxUID(); ++UID) {
+          CurSubmoduleIncludeState->NumIncludes[UID] += ModNumIncludes[UID];
+          NumIncludes[UID] += ModNumIncludes[UID];
+        }
+      },
+      [](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 +1478,23 @@
   Record = new PreprocessingRecord(getSourceManager());
   addPPCallbacks(std::unique_ptr<PPCallbacks>(Record));
 }
+
+unsigned Preprocessor::GetNumIncludes(const FileEntry *File) {
+  return NumIncludes[File];
+}
+
+bool Preprocessor::FirstTimeLexingFile(const FileEntry *File) {
+  return GetNumIncludes(File) == 1;
+}
+
+void Preprocessor::IncrementIncludeCount(const FileEntry *File, unsigned Count) {
+  CurSubmoduleIncludeState->NumIncludes[File] += Count;
+  NumIncludes[File] += Count;
+}
+
+void Preprocessor::addSubmoduleInclude(Module *M, StringRef Filename,
+                                       unsigned Count) {
+  if (auto MaybeFE = FileMgr.getFile(Filename))
+    (M == nullptr ? NumIncludes
+                  : SubmoduleIncludeStates[M].NumIncludes)[*MaybeFE] += Count;
+}
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)
+      GetNumIncludes(*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
@@ -92,9 +92,9 @@
   unsigned NumOnceOnlyFiles = 0, MaxNumIncludes = 0, NumSingleIncludedFiles = 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;
+//    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"
@@ -1203,7 +1203,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 +1360,7 @@
     FileInfo.isImport = true;
 
     // Has this already been #import'ed or #include'd?
-    if (FileInfo.NumIncludes && !TryEnterImported())
+    if (PP.GetNumIncludes(File) && !TryEnterImported())
       return false;
   } else {
     // Otherwise, if this is a #include of a file that was previously #import'd
@@ -1385,7 +1384,7 @@
   }
 
   // Increment the number of times this file has been included.
-  ++FileInfo.NumIncludes;
+  PP.IncrementIncludeCount(File);
 
   return true;
 }
Index: clang/include/clang/Serialization/ASTReader.h
===================================================================
--- clang/include/clang/Serialization/ASTReader.h
+++ clang/include/clang/Serialization/ASTReader.h
@@ -855,6 +855,11 @@
   /// The pragma clang optimize location (if the pragma state is "off").
   SourceLocation OptimizeOffPragmaLocation;
 
+  /// The submodule include maps. Translates local submodule ID to the map that
+  /// contains the number of includes for a file.
+  // TODO: Use plain vector, submodule IDs are small and dense.
+  llvm::DenseMap<unsigned, llvm::StringMap<unsigned>> SubmoduleImports;
+
   /// The PragmaMSStructKind pragma ms_struct state if set, or -1.
   int PragmaMSStructState = -1;
 
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 submodule includes.
+  SUBMODULE_INCLUDES = 66,
 };
 
 /// Record types used within a source manager block.
Index: clang/include/clang/Lex/Preprocessor.h
===================================================================
--- clang/include/clang/Lex/Preprocessor.h
+++ clang/include/clang/Lex/Preprocessor.h
@@ -717,6 +717,61 @@
   /// This mapping lives within the \p CurSubmoduleState.
   using MacroMap = llvm::DenseMap<const IdentifierInfo *, MacroState>;
 
+  /// Efficient map with FileEntry keys.
+  template <typename T> class FileEntryMap {
+    /// The underlying storage. Entries are indexed by FileEntry UID.
+    std::vector<T> UIDToValue;
+
+  public:
+    /// Create an empty FileEntry map.
+    FileEntryMap() = default;
+
+    /// Get the value for the given FileEntry key.
+    T &operator[](const FileEntry *File) { return operator[](File->getUID()); }
+
+    /// Get the value for the given FileEntry UID.
+    T &operator[](unsigned UID) {
+      ensureContains(UID);
+      return UIDToValue[UID];
+    }
+
+    /// Get the largest stored UID.
+    size_t maxUID() const { return UIDToValue.size() - 1; }
+
+    /// Get all FileEntry-value pairs stored in the map.
+    std::vector<std::pair<const FileEntry *, T>> getAll(FileManager &FM) const {
+      llvm::SmallVector<const FileEntry *, 0> UIDToFileEntry;
+      FM.GetUniqueIDMapping(UIDToFileEntry);
+
+      std::vector<std::pair<const FileEntry *, T>> Result;
+      for (unsigned UID = 0; UID < UIDToValue.size(); ++UID)
+        if (UIDToValue[UID] > 0)
+          Result.emplace_back(UIDToFileEntry[UID], UIDToValue[UID]);
+      return Result;
+    }
+
+  private:
+    /// Determine whether the underlying storage contains a value for FileEntry
+    /// with the given UID.
+    bool contains(unsigned UID) const { return UID < UIDToValue.size(); }
+
+    /// Ensure the storage contains a value for FileEntry with the given UID.
+    void ensureContains(unsigned UID) {
+      if (!contains(UID))
+        UIDToValue.resize(UID + 1);
+    }
+  };
+
+  /// In each submodule, we track the number of includes of a header file and
+  /// the set of visible modules.
+  struct SubmoduleIncludeState {
+    /// Map between FileEntries and the number of times the file was included.
+    FileEntryMap<unsigned> NumIncludes;
+
+    /// The set of modules that are visible within the submodule.
+    VisibleModuleSet VisibleModules;
+  };
+
   struct SubmoduleState;
 
   /// Information about a submodule that we're currently building.
@@ -736,12 +791,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 +825,20 @@
   /// in a submodule.
   SubmoduleState *CurSubmoduleState;
 
+  /// The include state for each entered submodule.
+  std::map<Module *, SubmoduleIncludeState> SubmoduleIncludeStates;
+
+  /// The include state for preprocessing outside of any submodule.
+  SubmoduleIncludeState NullSubmoduleIncludeState;
+
+  /// The current include state. Will be \p NullSubmoduleIncludeState if we're
+  /// not in a submodule.
+  SubmoduleIncludeState *CurSubmoduleIncludeState;
+
+  /// The global preprocessor state tracking the number of includes across
+  /// submodules.
+  FileEntryMap<unsigned> NumIncludes;
+
   /// The set of known macros exported from modules.
   llvm::FoldingSet<ModuleMacro> ModuleMacros;
 
@@ -1224,6 +1298,31 @@
 
   /// \}
 
+  /// Return the number of times the given file has been entered.
+  unsigned GetNumIncludes(const FileEntry *File);
+
+  /// Return true if this is the first time encountering this header.
+  bool FirstTimeLexingFile(const FileEntry *File);
+
+  /// Increment the count for the number of times the specified
+  /// FileEntry has been entered.
+  void IncrementIncludeCount(const FileEntry *File, unsigned Count = 1);
+
+  /// Get the submodule include information.
+  const std::map<Module *, SubmoduleIncludeState> &
+  getSubmoduleIncludeStates() const {
+    return SubmoduleIncludeStates;
+  }
+
+  /// Get the null include information.
+  // TODO: Merge this into \c getSubmoduleIncludeStates().
+  const SubmoduleIncludeState &getNullSubmoduleIncludeState() const {
+    return NullSubmoduleIncludeState;
+  }
+
+  /// Add a submodule include.
+  void addSubmoduleInclude(Module *M, StringRef Filename, unsigned NumIncludes);
+
   /// 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.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to