Index: clang/include/clang/Basic/FileEntry.h =================================================================== --- clang/include/clang/Basic/FileEntry.h +++ clang/include/clang/Basic/FileEntry.h @@ -185,6 +185,7 @@ bool hasOptionalValue() const { return ME; } friend struct llvm::DenseMapInfo; + friend struct FileEntryRefSameRefDenseMapInfo; struct dense_map_empty_tag {}; struct dense_map_tombstone_tag {}; @@ -274,6 +275,26 @@ namespace clang { +/// Specialization of \c DenseMapInfo for \c FileEntryRef that treats different +/// paths to the same underlying file independently, as with \c isSameRef. +struct FileEntryRefSameRefDenseMapInfo { + static inline FileEntryRef getEmptyKey() { + return FileEntryRef(FileEntryRef::dense_map_empty_tag()); + } + + static inline clang::FileEntryRef getTombstoneKey() { + return clang::FileEntryRef(clang::FileEntryRef::dense_map_tombstone_tag()); + } + + static unsigned getHashValue(clang::FileEntryRef Val) { + return hash_value(Val.ME); + } + + static bool isEqual(clang::FileEntryRef LHS, clang::FileEntryRef RHS) { + return LHS.isSameRef(RHS); + } +}; + /// Wrapper around Optional that degrades to 'const FileEntry*', /// facilitating incremental patches to propagate FileEntryRef. /// Index: clang/include/clang/Basic/SourceManager.h =================================================================== --- clang/include/clang/Basic/SourceManager.h +++ clang/include/clang/Basic/SourceManager.h @@ -126,29 +126,11 @@ /// One instance of this struct is kept for every file loaded or used. /// /// This object owns the MemoryBuffer object. -class alignas(8) ContentCache { +class ContentCache { /// The actual buffer containing the characters from the input /// file. mutable std::unique_ptr Buffer; - friend class FileInfo; - - /// Reference to the file entry representing this ContentCache. - /// - /// This reference does not own the FileEntry object. - /// - /// It is possible for this to be NULL if the ContentCache encapsulates - /// an imaginary text buffer. - /// - /// FIXME: Make non-optional using a virtual file as needed, remove \c - /// Filename and use \c OrigEntry.getNameAsRequested() instead. - OptionalFileEntryRefDegradesToFileEntryPtr OrigEntry; - - /// The filename that is used to access OrigEntry. - /// - /// FIXME: Remove this once OrigEntry is a FileEntryRef with a stable name. - StringRef Filename; - public: /// References the file which the contents were actually loaded from. /// @@ -181,13 +163,14 @@ mutable unsigned IsBufferInvalid : 1; ContentCache() - : OrigEntry(None), ContentsEntry(nullptr), BufferOverridden(false), - IsFileVolatile(false), IsTransient(false), IsBufferInvalid(false) {} + : ContentsEntry(nullptr), BufferOverridden(false), IsFileVolatile(false), + IsTransient(false), IsBufferInvalid(false) {} - ContentCache(FileEntryRef Ent) : ContentCache(Ent, Ent) {} + // FIXME: remove + ContentCache(FileEntryRef Ent) = delete; - ContentCache(FileEntryRef Ent, const FileEntry *contentEnt) - : OrigEntry(Ent), ContentsEntry(contentEnt), BufferOverridden(false), + ContentCache(const FileEntry *contentEnt) + : ContentsEntry(contentEnt), BufferOverridden(false), IsFileVolatile(false), IsTransient(false), IsBufferInvalid(false) {} /// Create a copy of \p Other with an unowned reference to its buffer as if @@ -262,10 +245,33 @@ static const char *getInvalidBOM(StringRef BufStr); }; -// Assert that the \c ContentCache objects will always be 8-byte aligned so +/// A \c ContentCache and the \c FileEntryRef that represents its name, needed +/// by \c FileInfo. This is stored out of line to keep \c SLocEntry small. +struct alignas(8) NamedContentCache { + /// The content cache entry. + ContentCache *Cache; + + /// Reference to the file entry representing \c ContentCache. + /// + /// This reference does not own the FileEntry object. + /// + /// It is possible for this to be NULL if the ContentCache encapsulates + /// an imaginary text buffer. + /// + /// FIXME: Make non-optional using a virtual file as needed, remove \c + /// Filename and use \c OrigEntry.getNameAsRequested() instead. + OptionalFileEntryRefDegradesToFileEntryPtr OrigEntry; + + /// The filename that is used to access OrigEntry. + /// + /// FIXME: Remove this once OrigEntry is a FileEntryRef with a stable name. + StringRef Filename; +}; + +// Assert that the \c NamedContentCache objects will always be 8-byte aligned so // that we can pack 3 bits of integer into pointers to such objects. -static_assert(alignof(ContentCache) >= 8, - "ContentCache must be 8-byte aligned."); +static_assert(alignof(NamedContentCache) >= 8, + "NamedContentCache must be 8-byte aligned."); /// Information about a FileID, basically just the logical file /// that it represents and include stack information. @@ -299,21 +305,20 @@ /// Whether this FileInfo has any \#line directives. unsigned HasLineDirectives : 1; - /// The content cache and the characteristic of the file. - llvm::PointerIntPair + /// The name, content cache and the characteristic of the file. + llvm::PointerIntPair ContentAndKind; public: /// Return a FileInfo object. - static FileInfo get(SourceLocation IL, ContentCache &Con, - CharacteristicKind FileCharacter, StringRef Filename) { + static FileInfo get(SourceLocation IL, NamedContentCache &Con, + CharacteristicKind FileCharacter) { FileInfo X; X.IncludeLoc = IL; X.NumCreatedFIDs = 0; X.HasLineDirectives = false; X.ContentAndKind.setPointer(&Con); X.ContentAndKind.setInt(FileCharacter); - Con.Filename = Filename; return X; } @@ -321,10 +326,14 @@ return IncludeLoc; } - const ContentCache &getContentCache() const { + const NamedContentCache &getNamedContentCache() const { return *ContentAndKind.getPointer(); } + const ContentCache &getContentCache() const { + return *getNamedContentCache().Cache; + } + /// Return whether this is a system header or not. CharacteristicKind getFileCharacteristic() const { return ContentAndKind.getInt(); @@ -339,10 +348,10 @@ /// Returns the name of the file that was used when the file was loaded from /// the underlying file system. - StringRef getName() const { return getContentCache().Filename; } + StringRef getName() const { return getNamedContentCache().Filename; } OptionalFileEntryRefDegradesToFileEntryPtr getOrigEntry() const { - return getContentCache().OrigEntry; + return getNamedContentCache().OrigEntry; } }; @@ -640,6 +649,10 @@ mutable llvm::BumpPtrAllocator ContentCacheAlloc; + llvm::DenseMap + NamedFileInfos; + /// Memoized information about all of the files tracked by this /// SourceManager. /// @@ -777,7 +790,8 @@ // Cache for the "fake" buffer used for error-recovery purposes. mutable std::unique_ptr FakeBufferForRecovery; - mutable std::unique_ptr FakeContentCacheForRecovery; + mutable std::unique_ptr + FakeContentCacheForRecovery; mutable std::unique_ptr FakeSLocEntryForRecovery; @@ -1789,7 +1803,7 @@ friend class ASTWriter; llvm::MemoryBufferRef getFakeBufferForRecovery() const; - SrcMgr::ContentCache &getFakeContentCacheForRecovery() const; + SrcMgr::NamedContentCache &getFakeContentCacheForRecovery() const; const SrcMgr::SLocEntry &loadSLocEntry(unsigned Index, bool *Invalid) const; @@ -1862,16 +1876,16 @@ /// /// This works regardless of whether the ContentCache corresponds to a /// file or some other input source. - FileID createFileIDImpl(SrcMgr::ContentCache &File, StringRef Filename, + FileID createFileIDImpl(SrcMgr::NamedContentCache &File, SourceLocation IncludePos, SrcMgr::CharacteristicKind DirCharacter, int LoadedID, SourceLocation::UIntTy LoadedOffset); - SrcMgr::ContentCache &getOrCreateContentCache(FileEntryRef SourceFile, - bool isSystemFile = false); + SrcMgr::NamedContentCache &getOrCreateContentCache(FileEntryRef SourceFile, + bool isSystemFile = false); /// Create a new ContentCache for the specified memory buffer. - SrcMgr::ContentCache & + SrcMgr::NamedContentCache & createMemBufferContentCache(std::unique_ptr Buf); FileID getFileIDSlow(SourceLocation::UIntTy SLocOffset) const; Index: clang/lib/Basic/SourceManager.cpp =================================================================== --- clang/lib/Basic/SourceManager.cpp +++ clang/lib/Basic/SourceManager.cpp @@ -360,7 +360,6 @@ ContentCache ContentCache::createUnownedCopy(const ContentCache &Other) { ContentCache Clone; - Clone.OrigEntry = Other.OrigEntry; Clone.ContentsEntry = Other.ContentsEntry; Clone.BufferOverridden = Other.BufferOverridden; Clone.IsFileVolatile = Other.IsFileVolatile; @@ -391,47 +390,70 @@ } } -ContentCache &SourceManager::getOrCreateContentCache(FileEntryRef FileEnt, - bool isSystemFile) { +NamedContentCache & +SourceManager::getOrCreateContentCache(FileEntryRef SourceFile, + bool isSystemFile) { + NamedContentCache *&NamedEntry = NamedFileInfos[SourceFile]; + if (NamedEntry) + return *NamedEntry; + + NamedEntry = ContentCacheAlloc.Allocate(); + NamedEntry->OrigEntry = SourceFile; + NamedEntry->Filename = SourceFile.getName(); + + if (OverriddenFilesInfo && !OverridenFilesKeepOriginalName) { + auto overI = OverriddenFilesInfo->OverriddenFiles.find(SourceFile); + if (overI != OverriddenFilesInfo->OverriddenFiles.end()) { + NamedEntry->OrigEntry = overI->second; + NamedEntry->Filename = overI->second.getName(); + } + } + // Do we already have information about this file? - ContentCache *&Entry = FileInfos[FileEnt]; - if (Entry) - return *Entry; + ContentCache *&Entry = FileInfos[&SourceFile.getFileEntry()]; + if (Entry) { + NamedEntry->Cache = Entry; + return *NamedEntry; + } // Nope, create a new Cache entry. + Entry = ContentCacheAlloc.Allocate(); + NamedEntry->Cache = Entry; if (OverriddenFilesInfo) { // If the file contents are overridden with contents from another file, // pass that file to ContentCache. - auto overI = OverriddenFilesInfo->OverriddenFiles.find(FileEnt); + auto overI = OverriddenFilesInfo->OverriddenFiles.find(SourceFile); if (overI == OverriddenFilesInfo->OverriddenFiles.end()) - new (Entry) ContentCache(FileEnt); + new (Entry) ContentCache(&SourceFile.getFileEntry()); else - new (Entry) ContentCache(OverridenFilesKeepOriginalName ? FileEnt - : overI->second, - overI->second); + new (Entry) ContentCache(&overI->second.getFileEntry()); } else { - new (Entry) ContentCache(FileEnt); + new (Entry) ContentCache(&SourceFile.getFileEntry()); } Entry->IsFileVolatile = UserFilesAreVolatile && !isSystemFile; Entry->IsTransient = FilesAreTransient; - Entry->BufferOverridden |= FileEnt.isNamedPipe(); + Entry->BufferOverridden |= SourceFile.isNamedPipe(); - return *Entry; + return *NamedEntry; } /// Create a new ContentCache for the specified memory buffer. /// This does no caching. -ContentCache &SourceManager::createMemBufferContentCache( +NamedContentCache &SourceManager::createMemBufferContentCache( std::unique_ptr Buffer) { // Add a new ContentCache to the MemBufferInfos list and return it. - ContentCache *Entry = ContentCacheAlloc.Allocate(); - new (Entry) ContentCache(); + NamedContentCache *NamedEntry = + new (ContentCacheAlloc.Allocate()) NamedContentCache(); + NamedEntry->Filename = Buffer->getBufferIdentifier(); + ContentCache *Entry = + new (ContentCacheAlloc.Allocate()) ContentCache(); MemBufferInfos.push_back(Entry); Entry->setBuffer(std::move(Buffer)); - return *Entry; + NamedEntry->Cache = Entry; + return *NamedEntry; } const SrcMgr::SLocEntry &SourceManager::loadSLocEntry(unsigned Index, @@ -446,7 +468,7 @@ if (!FakeSLocEntryForRecovery) FakeSLocEntryForRecovery = std::make_unique(SLocEntry::get( 0, FileInfo::get(SourceLocation(), getFakeContentCacheForRecovery(), - SrcMgr::C_User, ""))); + SrcMgr::C_User))); return *FakeSLocEntryForRecovery; } } @@ -482,10 +504,13 @@ /// As part of recovering from missing or changed content, produce a /// fake content cache. -SrcMgr::ContentCache &SourceManager::getFakeContentCacheForRecovery() const { +SrcMgr::NamedContentCache & +SourceManager::getFakeContentCacheForRecovery() const { if (!FakeContentCacheForRecovery) { - FakeContentCacheForRecovery = std::make_unique(); - FakeContentCacheForRecovery->setUnownedBuffer(getFakeBufferForRecovery()); + FakeContentCacheForRecovery = std::make_unique(); + FakeContentCacheForRecovery->Cache = new SrcMgr::ContentCache(); + FakeContentCacheForRecovery->Cache->setUnownedBuffer( + getFakeBufferForRecovery()); } return *FakeContentCacheForRecovery; } @@ -549,16 +574,16 @@ SrcMgr::CharacteristicKind FileCharacter, int LoadedID, SourceLocation::UIntTy LoadedOffset) { - SrcMgr::ContentCache &IR = getOrCreateContentCache(SourceFile, - isSystem(FileCharacter)); + SrcMgr::NamedContentCache &IR = + getOrCreateContentCache(SourceFile, isSystem(FileCharacter)); // If this is a named pipe, immediately load the buffer to ensure subsequent // calls to ContentCache::getSize() are accurate. - if (IR.ContentsEntry->isNamedPipe()) - (void)IR.getBufferOrNone(Diag, getFileManager(), SourceLocation()); + if (IR.Cache->ContentsEntry->isNamedPipe()) + (void)IR.Cache->getBufferOrNone(Diag, getFileManager(), SourceLocation()); - return createFileIDImpl(IR, SourceFile.getName(), IncludePos, FileCharacter, - LoadedID, LoadedOffset); + return createFileIDImpl(IR, IncludePos, FileCharacter, LoadedID, + LoadedOffset); } /// Create a new FileID that represents the specified memory buffer. @@ -570,8 +595,7 @@ int LoadedID, SourceLocation::UIntTy LoadedOffset, SourceLocation IncludeLoc) { - StringRef Name = Buffer->getBufferIdentifier(); - return createFileIDImpl(createMemBufferContentCache(std::move(Buffer)), Name, + return createFileIDImpl(createMemBufferContentCache(std::move(Buffer)), IncludeLoc, FileCharacter, LoadedID, LoadedOffset); } @@ -601,7 +625,7 @@ /// createFileID - Create a new FileID for the specified ContentCache and /// include position. This works regardless of whether the ContentCache /// corresponds to a file or some other input source. -FileID SourceManager::createFileIDImpl(ContentCache &File, StringRef Filename, +FileID SourceManager::createFileIDImpl(NamedContentCache &File, SourceLocation IncludePos, SrcMgr::CharacteristicKind FileCharacter, int LoadedID, @@ -612,19 +636,18 @@ assert(Index < LoadedSLocEntryTable.size() && "FileID out of range"); assert(!SLocEntryLoaded[Index] && "FileID already loaded"); LoadedSLocEntryTable[Index] = SLocEntry::get( - LoadedOffset, FileInfo::get(IncludePos, File, FileCharacter, Filename)); + LoadedOffset, FileInfo::get(IncludePos, File, FileCharacter)); SLocEntryLoaded[Index] = true; return FileID::get(LoadedID); } - unsigned FileSize = File.getSize(); + unsigned FileSize = File.Cache->getSize(); if (!(NextLocalOffset + FileSize + 1 > NextLocalOffset && NextLocalOffset + FileSize + 1 <= CurrentLoadedOffset)) { Diag.Report(IncludePos, diag::err_include_too_large); return FileID(); } - LocalSLocEntryTable.push_back( - SLocEntry::get(NextLocalOffset, - FileInfo::get(IncludePos, File, FileCharacter, Filename))); + LocalSLocEntryTable.push_back(SLocEntry::get( + NextLocalOffset, FileInfo::get(IncludePos, File, FileCharacter))); // We do a +1 here because we want a SourceLocation that means "the end of the // file", e.g. for the "no newline at the end of the file" diagnostic. NextLocalOffset += FileSize + 1; @@ -686,16 +709,17 @@ llvm::Optional SourceManager::getMemoryBufferForFileOrNone(const FileEntry *File) { - SrcMgr::ContentCache &IR = getOrCreateContentCache(File->getLastRef()); - return IR.getBufferOrNone(Diag, getFileManager(), SourceLocation()); + SrcMgr::NamedContentCache &IR = getOrCreateContentCache(File->getLastRef()); + return IR.Cache->getBufferOrNone(Diag, getFileManager(), SourceLocation()); } void SourceManager::overrideFileContents( const FileEntry *SourceFile, std::unique_ptr Buffer) { - SrcMgr::ContentCache &IR = getOrCreateContentCache(SourceFile->getLastRef()); + SrcMgr::NamedContentCache &IR = + getOrCreateContentCache(SourceFile->getLastRef()); - IR.setBuffer(std::move(Buffer)); - IR.BufferOverridden = true; + IR.Cache->setBuffer(std::move(Buffer)); + IR.Cache->BufferOverridden = true; getOverriddenFilesInfo().OverriddenFilesWithBuffer.insert(SourceFile); } @@ -729,7 +753,7 @@ } void SourceManager::setFileIsTransient(const FileEntry *File) { - getOrCreateContentCache(File->getLastRef()).IsTransient = true; + getOrCreateContentCache(File->getLastRef()).Cache->IsTransient = true; } Optional Index: clang/lib/Serialization/ASTReader.cpp =================================================================== --- clang/lib/Serialization/ASTReader.cpp +++ clang/lib/Serialization/ASTReader.cpp @@ -1550,11 +1550,11 @@ NumFileDecls)); } - const SrcMgr::ContentCache &ContentCache = + const SrcMgr::NamedContentCache &ContentCache = SourceMgr.getOrCreateContentCache(*File, isSystem(FileCharacter)); - if (OverriddenBuffer && !ContentCache.BufferOverridden && - ContentCache.ContentsEntry == FileInfo.getOrigEntry() && - !ContentCache.getBufferIfLoaded()) { + if (OverriddenBuffer && !ContentCache.Cache->BufferOverridden && + ContentCache.Cache->ContentsEntry == FileInfo.getOrigEntry() && + !ContentCache.Cache->getBufferIfLoaded()) { auto Buffer = ReadBuffer(SLocEntryCursor, File->getName()); if (!Buffer) return true; Index: clang/test/Frontend/show-includes-symlink.c =================================================================== --- /dev/null +++ clang/test/Frontend/show-includes-symlink.c @@ -0,0 +1,10 @@ +// REQUIRES: shell +// RUN: rm -rf %t && mkdir %t +// RUN: touch %t/A.h +// RUN: ln -s A.h %t/B.h +// RUN: %clang_cc1 -I%t --show-includes -E -o /dev/null %s 2>&1 | FileCheck %s + +#include "A.h" +// CHECK: Note: including file: {{.*}}A.h +#include "B.h" +// CHECK: Note: including file: {{.*}}B.h Index: clang/test/Preprocessor/header-symlink.c =================================================================== --- /dev/null +++ clang/test/Preprocessor/header-symlink.c @@ -0,0 +1,18 @@ +// REQUIRES: shell +// RUN: rm -rf %t && mkdir %t +// RUN: touch %t/A.h +// RUN: ln -s A.h %t/B.h +// RUN: %clang_cc1 -I%t -E %s | FileCheck %s + +int A_table[] = { + #include "A.h" +// CHECK: # 1 "{{.*}}A.h" 1 +}; +int B_table[] = { + #include "B.h" +// CHECK: # 1 "{{.*}}B.h" 1 +}; +int A_table[] = { + #include "A.h" +// CHECK: # 1 "{{.*}}A.h" 1 +}; \ No newline at end of file