Index: clang/include/clang/Index/ParsedSrcLocationsTracker.h =================================================================== --- /dev/null +++ clang/include/clang/Index/ParsedSrcLocationsTracker.h @@ -0,0 +1,169 @@ +//===- ParsedSrcLocationsTracker.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_PARSEDSRCLOCATIONSTRACKER_H +#define LLVM_CLANG_INDEX_PARSEDSRCLOCATIONSTRACKER_H + +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/PPConditionalDirectiveRecord.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/FileSystem.h" + +#include + +namespace clang { +namespace index { + +/// A "region" in source code identified by the file/offset of the +/// preprocessor conditional directive that it belongs to. +/// Multiple, non-consecutive ranges can be parts of the same region. +/// +/// As an example of different regions separated by preprocessor directives: +/// +/// \code +/// #1 +/// #ifdef BLAH +/// #2 +/// #ifdef CAKE +/// #3 +/// #endif +/// #2 +/// #endif +/// #1 +/// \endcode +/// +/// There are 3 regions, with non-consecutive parts: +/// #1 is identified as the beginning of the file +/// #2 is identified as the location of "#ifdef BLAH" +/// #3 is identified as the location of "#ifdef CAKE" + +class PPRegion { + llvm::sys::fs::UniqueID UniqueID; + time_t ModTime; + unsigned Offset; +public: + PPRegion() : UniqueID(0, 0), ModTime(), Offset() {} + PPRegion(llvm::sys::fs::UniqueID UniqueID, unsigned offset, time_t modTime) + : UniqueID(UniqueID), ModTime(modTime), Offset(offset) {} + + const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; } + unsigned getOffset() const { return Offset; } + time_t getModTime() const { return ModTime; } + + bool isInvalid() const { return *this == PPRegion(); } + + friend bool operator==(const PPRegion &lhs, const PPRegion &rhs) { + return lhs.UniqueID == rhs.UniqueID && lhs.Offset == rhs.Offset && + lhs.ModTime == rhs.ModTime; + } +}; + +typedef llvm::DenseSet PPRegionSetTy; + +} // namespace index +} // namespace clang + +namespace llvm { + using clang::index::PPRegion; + + template <> + struct DenseMapInfo { + static inline PPRegion getEmptyKey() { + return PPRegion(llvm::sys::fs::UniqueID(0, 0), unsigned(-1), 0); + } + static inline PPRegion getTombstoneKey() { + return PPRegion(llvm::sys::fs::UniqueID(0, 0), unsigned(-2), 0); + } + + static unsigned getHashValue(const PPRegion &S) { + llvm::FoldingSetNodeID ID; + const llvm::sys::fs::UniqueID &UniqueID = S.getUniqueID(); + ID.AddInteger(UniqueID.getFile()); + ID.AddInteger(UniqueID.getDevice()); + ID.AddInteger(S.getOffset()); + ID.AddInteger(S.getModTime()); + return ID.ComputeHash(); + } + + static bool isEqual(const PPRegion &LHS, const PPRegion &RHS) { + return LHS == RHS; + } + }; +} // namespace llvm + +namespace clang { +namespace index { + +/// Keeps track of function bodies that have already been parsed. +class ThreadSafeParsedRegions { + std::mutex Mux; + PPRegionSetTy ParsedRegions; + +public: + ~ThreadSafeParsedRegions() = default; + + void copyTo(PPRegionSetTy &Set) { + std::lock_guard MG(Mux); + Set = ParsedRegions; + } + + void addParsedRegions(ArrayRef Regions) { + std::lock_guard MG(Mux); + ParsedRegions.insert(Regions.begin(), Regions.end()); + } +}; + +/// Provides information whether source locations have already been parsed in +/// another FrontendAction. +/// +/// Is NOT thread-safe. +class ParsedSrcLocationsTracker { + ThreadSafeParsedRegions &ParsedRegionsStorage; + PPConditionalDirectiveRecord &PPRec; + Preprocessor &PP; + + /// Snapshot of the shared state at the point when this instance was + /// constructed. + PPRegionSetTy ParsedRegions; + /// Regions that were queried during this instance lifetime. + SmallVector NewParsedRegions; + + /// Caching the last queried region. + PPRegion LastRegion; + bool LastIsParsed; + +public: + /// Creates snapshot of \p ParsedRegionsStorage. + ParsedSrcLocationsTracker(ThreadSafeParsedRegions &ParsedRegionsStorage, + PPConditionalDirectiveRecord &ppRec, + Preprocessor &pp) + : ParsedRegionsStorage(ParsedRegionsStorage), PPRec(ppRec), PP(pp) { + ParsedRegionsStorage.copyTo(ParsedRegions); + } + + /// \returns true iff \p Loc has already been parsed. + /// + /// Can provide false-negative in case the location was parsed after this + /// instance had been constructed. + bool hasAlredyBeenParsed(SourceLocation Loc, FileID FID, + const FileEntry *FE); + + /// Updates ParsedRegionsStorage with newly parsed regions. + void syncWithStorage(); +private: + PPRegion getRegion(SourceLocation Loc, FileID FID, const FileEntry *FE); + bool isParsedOnceInclude(const FileEntry *FE); +}; + +} // namespace index +} // namespace clang + +#endif // LLVM_CLANG_INDEX_SKIPALREADYINDEXEDCODE_H Index: clang/lib/Index/CMakeLists.txt =================================================================== --- clang/lib/Index/CMakeLists.txt +++ clang/lib/Index/CMakeLists.txt @@ -13,6 +13,7 @@ IndexingContext.cpp IndexSymbol.cpp IndexTypeSourceInfo.cpp + ParsedSrcLocationsTracker.cpp USRGeneration.cpp ADDITIONAL_HEADERS Index: clang/lib/Index/ParsedSrcLocationsTracker.cpp =================================================================== --- /dev/null +++ clang/lib/Index/ParsedSrcLocationsTracker.cpp @@ -0,0 +1,70 @@ +//===- ParsedSrcLocationsTracker.cpp ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/ParsedSrcLocationsTracker.h" + +namespace clang { +namespace index { + +bool ParsedSrcLocationsTracker::hasAlredyBeenParsed(SourceLocation Loc, FileID FID, + const FileEntry *FE) { + assert(FE); + PPRegion region = getRegion(Loc, FID, FE); + if (region.isInvalid()) + return false; + + // Check common case, consecutive functions in the same region. + if (LastRegion == region) + return LastIsParsed; + + LastRegion = region; + // Source locations can't be revisited during single TU parsing. + // That means if we hit the same region again, it's a different location in + // the same region and so the "is parsed" value from the snapshot is still + // correct. + LastIsParsed = ParsedRegions.count(region); + if (!LastIsParsed) + NewParsedRegions.emplace_back(std::move(region)); + return LastIsParsed; +} + +void ParsedSrcLocationsTracker::syncWithStorage() { ParsedRegionsStorage.addParsedRegions(NewParsedRegions); } + +PPRegion ParsedSrcLocationsTracker::getRegion(SourceLocation Loc, FileID FID, const FileEntry *FE) { + assert(FE); + auto Bail = [this, FE]() { + if (isParsedOnceInclude(FE)) { + const llvm::sys::fs::UniqueID &ID = FE->getUniqueID(); + return PPRegion(ID, 0, FE->getModificationTime()); + } + return PPRegion(); + }; + + SourceLocation RegionLoc = PPRec.findConditionalDirectiveRegionLoc(Loc); + assert(RegionLoc.isFileID()); + if (RegionLoc.isInvalid()) + return Bail(); + + FileID RegionFID; + unsigned RegionOffset; + std::tie(RegionFID, RegionOffset) = + PPRec.getSourceManager().getDecomposedLoc(RegionLoc); + + if (RegionFID != FID) + return Bail(); + + const llvm::sys::fs::UniqueID &ID = FE->getUniqueID(); + return PPRegion(ID, RegionOffset, FE->getModificationTime()); +} + +bool ParsedSrcLocationsTracker::isParsedOnceInclude(const FileEntry *FE) { + return PP.getHeaderSearchInfo().isFileMultipleIncludeGuarded(FE); +} + +} // namespace index +} // namespace clang Index: clang/tools/libclang/Indexing.cpp =================================================================== --- clang/tools/libclang/Indexing.cpp +++ clang/tools/libclang/Indexing.cpp @@ -21,6 +21,7 @@ #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/Utils.h" #include "clang/Index/IndexingAction.h" +#include "clang/Index/ParsedSrcLocationsTracker.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/PPConditionalDirectiveRecord.h" @@ -39,198 +40,6 @@ namespace { -//===----------------------------------------------------------------------===// -// Skip Parsed Bodies -//===----------------------------------------------------------------------===// - -/// A "region" in source code identified by the file/offset of the -/// preprocessor conditional directive that it belongs to. -/// Multiple, non-consecutive ranges can be parts of the same region. -/// -/// As an example of different regions separated by preprocessor directives: -/// -/// \code -/// #1 -/// #ifdef BLAH -/// #2 -/// #ifdef CAKE -/// #3 -/// #endif -/// #2 -/// #endif -/// #1 -/// \endcode -/// -/// There are 3 regions, with non-consecutive parts: -/// #1 is identified as the beginning of the file -/// #2 is identified as the location of "#ifdef BLAH" -/// #3 is identified as the location of "#ifdef CAKE" -/// -class PPRegion { - llvm::sys::fs::UniqueID UniqueID; - time_t ModTime; - unsigned Offset; -public: - PPRegion() : UniqueID(0, 0), ModTime(), Offset() {} - PPRegion(llvm::sys::fs::UniqueID UniqueID, unsigned offset, time_t modTime) - : UniqueID(UniqueID), ModTime(modTime), Offset(offset) {} - - const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; } - unsigned getOffset() const { return Offset; } - time_t getModTime() const { return ModTime; } - - bool isInvalid() const { return *this == PPRegion(); } - - friend bool operator==(const PPRegion &lhs, const PPRegion &rhs) { - return lhs.UniqueID == rhs.UniqueID && lhs.Offset == rhs.Offset && - lhs.ModTime == rhs.ModTime; - } -}; - -typedef llvm::DenseSet PPRegionSetTy; - -} // end anonymous namespace - -namespace llvm { - - template <> - struct DenseMapInfo { - static inline PPRegion getEmptyKey() { - return PPRegion(llvm::sys::fs::UniqueID(0, 0), unsigned(-1), 0); - } - static inline PPRegion getTombstoneKey() { - return PPRegion(llvm::sys::fs::UniqueID(0, 0), unsigned(-2), 0); - } - - static unsigned getHashValue(const PPRegion &S) { - llvm::FoldingSetNodeID ID; - const llvm::sys::fs::UniqueID &UniqueID = S.getUniqueID(); - ID.AddInteger(UniqueID.getFile()); - ID.AddInteger(UniqueID.getDevice()); - ID.AddInteger(S.getOffset()); - ID.AddInteger(S.getModTime()); - return ID.ComputeHash(); - } - - static bool isEqual(const PPRegion &LHS, const PPRegion &RHS) { - return LHS == RHS; - } - }; -} - -namespace { - -/// Keeps track of function bodies that have already been parsed. -/// -/// Is thread-safe. -class SharedParsedRegionsStorage { - std::mutex Mux; - PPRegionSetTy ParsedRegions; - -public: - ~SharedParsedRegionsStorage() = default; - - void copyTo(PPRegionSetTy &Set) { - std::lock_guard MG(Mux); - Set = ParsedRegions; - } - - void merge(ArrayRef Regions) { - std::lock_guard MG(Mux); - ParsedRegions.insert(Regions.begin(), Regions.end()); - } -}; - -/// Provides information whether source locations have already been parsed in -/// another FrontendAction. -/// -/// Is NOT thread-safe. -class ParsedSrcLocationsTracker { - SharedParsedRegionsStorage &ParsedRegionsStorage; - PPConditionalDirectiveRecord &PPRec; - Preprocessor &PP; - - /// Snapshot of the shared state at the point when this instance was - /// constructed. - PPRegionSetTy ParsedRegions; - /// Regions that were queried during this instance lifetime. - SmallVector NewParsedRegions; - - /// Caching the last queried region. - PPRegion LastRegion; - bool LastIsParsed; - -public: - /// Creates snapshot of \p ParsedRegionsStorage. - ParsedSrcLocationsTracker(SharedParsedRegionsStorage &ParsedRegionsStorage, - PPConditionalDirectiveRecord &ppRec, - Preprocessor &pp) - : ParsedRegionsStorage(ParsedRegionsStorage), PPRec(ppRec), PP(pp) { - ParsedRegionsStorage.copyTo(ParsedRegions); - } - - /// \returns true iff \p Loc has already been parsed. - /// - /// Can provide false-negative in case the location was parsed after this - /// instance had been constructed. - bool hasAlredyBeenParsed(SourceLocation Loc, FileID FID, - const FileEntry *FE) { - assert(FE); - PPRegion region = getRegion(Loc, FID, FE); - if (region.isInvalid()) - return false; - - // Check common case, consecutive functions in the same region. - if (LastRegion == region) - return LastIsParsed; - - LastRegion = region; - // Source locations can't be revisited during single TU parsing. - // That means if we hit the same region again, it's a different location in - // the same region and so the "is parsed" value from the snapshot is still - // correct. - LastIsParsed = ParsedRegions.count(region); - if (!LastIsParsed) - NewParsedRegions.emplace_back(std::move(region)); - return LastIsParsed; - } - - /// Updates ParsedRegionsStorage with newly parsed regions. - void syncWithStorage() { ParsedRegionsStorage.merge(NewParsedRegions); } - -private: - PPRegion getRegion(SourceLocation Loc, FileID FID, const FileEntry *FE) { - assert(FE); - auto Bail = [this, FE]() { - if (isParsedOnceInclude(FE)) { - const llvm::sys::fs::UniqueID &ID = FE->getUniqueID(); - return PPRegion(ID, 0, FE->getModificationTime()); - } - return PPRegion(); - }; - - SourceLocation RegionLoc = PPRec.findConditionalDirectiveRegionLoc(Loc); - assert(RegionLoc.isFileID()); - if (RegionLoc.isInvalid()) - return Bail(); - - FileID RegionFID; - unsigned RegionOffset; - std::tie(RegionFID, RegionOffset) = - PPRec.getSourceManager().getDecomposedLoc(RegionLoc); - - if (RegionFID != FID) - return Bail(); - - const llvm::sys::fs::UniqueID &ID = FE->getUniqueID(); - return PPRegion(ID, RegionOffset, FE->getModificationTime()); - } - - bool isParsedOnceInclude(const FileEntry *FE) { - return PP.getHeaderSearchInfo().isFileMultipleIncludeGuarded(FE); - } -}; - //===----------------------------------------------------------------------===// // IndexPPCallbacks //===----------------------------------------------------------------------===// @@ -367,13 +176,11 @@ class IndexingFrontendAction : public ASTFrontendAction { std::shared_ptr DataConsumer; - - SharedParsedRegionsStorage *SKData; - std::unique_ptr ParsedLocsTracker; + ThreadSafeParsedRegions *SKData; public: IndexingFrontendAction(std::shared_ptr dataConsumer, - SharedParsedRegionsStorage *skData) + ThreadSafeParsedRegions *skData) : DataConsumer(std::move(dataConsumer)), SKData(skData) {} std::unique_ptr CreateASTConsumer(CompilerInstance &CI, @@ -391,15 +198,15 @@ PP.addPPCallbacks(std::make_unique(PP, *DataConsumer)); DataConsumer->setPreprocessor(CI.getPreprocessorPtr()); + std::unique_ptr ParsedLocsTracker; if (SKData) { auto *PPRec = new PPConditionalDirectiveRecord(PP.getSourceManager()); PP.addPPCallbacks(std::unique_ptr(PPRec)); - ParsedLocsTracker = - std::make_unique(*SKData, *PPRec, PP); + ParsedLocsTracker = std::make_unique(*SKData, *PPRec, PP); } return std::make_unique(*DataConsumer, - ParsedLocsTracker.get()); + ParsedLocsTracker.release()); } TranslationUnitKind getTranslationUnitKind() override { @@ -426,10 +233,10 @@ struct IndexSessionData { CXIndex CIdx; - std::unique_ptr SkipBodyData; + std::unique_ptr SkipBodyData; explicit IndexSessionData(CXIndex cIdx) - : CIdx(cIdx), SkipBodyData(new SharedParsedRegionsStorage) {} + : CIdx(cIdx), SkipBodyData(new ThreadSafeParsedRegions) {} }; } // anonymous namespace