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