1a7dea167SDimitry Andric //===- DependencyScanningFilesystem.cpp - clang-scan-deps fs --------------===// 2a7dea167SDimitry Andric // 3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a7dea167SDimitry Andric // 7a7dea167SDimitry Andric //===----------------------------------------------------------------------===// 8a7dea167SDimitry Andric 9a7dea167SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" 10a7dea167SDimitry Andric #include "clang/Lex/DependencyDirectivesSourceMinimizer.h" 11a7dea167SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 120eae32dcSDimitry Andric #include "llvm/Support/SmallVectorMemoryBuffer.h" 13a7dea167SDimitry Andric #include "llvm/Support/Threading.h" 14a7dea167SDimitry Andric 15a7dea167SDimitry Andric using namespace clang; 16a7dea167SDimitry Andric using namespace tooling; 17a7dea167SDimitry Andric using namespace dependencies; 18a7dea167SDimitry Andric 19*04eeddc0SDimitry Andric llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry> 20*04eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::readFile(StringRef Filename) { 21a7dea167SDimitry Andric // Load the file and its content from the file system. 22*04eeddc0SDimitry Andric auto MaybeFile = getUnderlyingFS().openFileForRead(Filename); 23a7dea167SDimitry Andric if (!MaybeFile) 24a7dea167SDimitry Andric return MaybeFile.getError(); 250eae32dcSDimitry Andric auto File = std::move(*MaybeFile); 26a7dea167SDimitry Andric 270eae32dcSDimitry Andric auto MaybeStat = File->status(); 280eae32dcSDimitry Andric if (!MaybeStat) 290eae32dcSDimitry Andric return MaybeStat.getError(); 300eae32dcSDimitry Andric auto Stat = std::move(*MaybeStat); 310eae32dcSDimitry Andric 320eae32dcSDimitry Andric auto MaybeBuffer = File->getBuffer(Stat.getName()); 33a7dea167SDimitry Andric if (!MaybeBuffer) 34a7dea167SDimitry Andric return MaybeBuffer.getError(); 350eae32dcSDimitry Andric auto Buffer = std::move(*MaybeBuffer); 360eae32dcSDimitry Andric 37*04eeddc0SDimitry Andric // If the file size changed between read and stat, pretend it didn't. 38*04eeddc0SDimitry Andric if (Stat.getSize() != Buffer->getBufferSize()) 39*04eeddc0SDimitry Andric Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize()); 40*04eeddc0SDimitry Andric 41*04eeddc0SDimitry Andric return TentativeEntry(Stat, std::move(Buffer)); 420eae32dcSDimitry Andric } 430eae32dcSDimitry Andric 44*04eeddc0SDimitry Andric EntryRef DependencyScanningWorkerFilesystem::minimizeIfNecessary( 45*04eeddc0SDimitry Andric const CachedFileSystemEntry &Entry, StringRef Filename, bool Disable) { 46*04eeddc0SDimitry Andric if (Entry.isError() || Entry.isDirectory() || Disable || 47*04eeddc0SDimitry Andric !shouldMinimize(Filename, Entry.getUniqueID())) 48*04eeddc0SDimitry Andric return EntryRef(/*Minimized=*/false, Filename, Entry); 49*04eeddc0SDimitry Andric 50*04eeddc0SDimitry Andric CachedFileContents *Contents = Entry.getContents(); 51*04eeddc0SDimitry Andric assert(Contents && "contents not initialized"); 52*04eeddc0SDimitry Andric 53*04eeddc0SDimitry Andric // Double-checked locking. 54*04eeddc0SDimitry Andric if (Contents->MinimizedAccess.load()) 55*04eeddc0SDimitry Andric return EntryRef(/*Minimized=*/true, Filename, Entry); 56*04eeddc0SDimitry Andric 57*04eeddc0SDimitry Andric std::lock_guard<std::mutex> GuardLock(Contents->ValueLock); 58*04eeddc0SDimitry Andric 59*04eeddc0SDimitry Andric // Double-checked locking. 60*04eeddc0SDimitry Andric if (Contents->MinimizedAccess.load()) 61*04eeddc0SDimitry Andric return EntryRef(/*Minimized=*/true, Filename, Entry); 62a7dea167SDimitry Andric 63a7dea167SDimitry Andric llvm::SmallString<1024> MinimizedFileContents; 64a7dea167SDimitry Andric // Minimize the file down to directives that might affect the dependencies. 65a7dea167SDimitry Andric SmallVector<minimize_source_to_dependency_directives::Token, 64> Tokens; 66*04eeddc0SDimitry Andric if (minimizeSourceToDependencyDirectives(Contents->Original->getBuffer(), 670eae32dcSDimitry Andric MinimizedFileContents, Tokens)) { 680eae32dcSDimitry Andric // FIXME: Propagate the diagnostic if desired by the client. 690eae32dcSDimitry Andric // Use the original file if the minimization failed. 70*04eeddc0SDimitry Andric Contents->MinimizedStorage = 71*04eeddc0SDimitry Andric llvm::MemoryBuffer::getMemBuffer(*Contents->Original); 72*04eeddc0SDimitry Andric Contents->MinimizedAccess.store(Contents->MinimizedStorage.get()); 73*04eeddc0SDimitry Andric return EntryRef(/*Minimized=*/true, Filename, Entry); 74a7dea167SDimitry Andric } 75a7dea167SDimitry Andric 76a7dea167SDimitry Andric // The contents produced by the minimizer must be null terminated. 77a7dea167SDimitry Andric assert(MinimizedFileContents.data()[MinimizedFileContents.size()] == '\0' && 78a7dea167SDimitry Andric "not null terminated contents"); 79a7dea167SDimitry Andric 80a7dea167SDimitry Andric // Compute the skipped PP ranges that speedup skipping over inactive 81a7dea167SDimitry Andric // preprocessor blocks. 82a7dea167SDimitry Andric llvm::SmallVector<minimize_source_to_dependency_directives::SkippedRange, 32> 83a7dea167SDimitry Andric SkippedRanges; 84a7dea167SDimitry Andric minimize_source_to_dependency_directives::computeSkippedRanges(Tokens, 85a7dea167SDimitry Andric SkippedRanges); 86a7dea167SDimitry Andric PreprocessorSkippedRangeMapping Mapping; 87a7dea167SDimitry Andric for (const auto &Range : SkippedRanges) { 88a7dea167SDimitry Andric if (Range.Length < 16) { 89a7dea167SDimitry Andric // Ignore small ranges as non-profitable. 90a7dea167SDimitry Andric // FIXME: This is a heuristic, its worth investigating the tradeoffs 91a7dea167SDimitry Andric // when it should be applied. 92a7dea167SDimitry Andric continue; 93a7dea167SDimitry Andric } 94a7dea167SDimitry Andric Mapping[Range.Offset] = Range.Length; 95a7dea167SDimitry Andric } 96*04eeddc0SDimitry Andric Contents->PPSkippedRangeMapping = std::move(Mapping); 97a7dea167SDimitry Andric 98*04eeddc0SDimitry Andric Contents->MinimizedStorage = std::make_unique<llvm::SmallVectorMemoryBuffer>( 990eae32dcSDimitry Andric std::move(MinimizedFileContents)); 100*04eeddc0SDimitry Andric // This function performed double-checked locking using `MinimizedAccess`. 101*04eeddc0SDimitry Andric // Assigning it must be the last thing this function does. If we were to 102*04eeddc0SDimitry Andric // assign it before `PPSkippedRangeMapping`, other threads may skip the 103*04eeddc0SDimitry Andric // critical section (`MinimizedAccess != nullptr`) and access the mappings 104*04eeddc0SDimitry Andric // that are about to be initialized, leading to a data race. 105*04eeddc0SDimitry Andric Contents->MinimizedAccess.store(Contents->MinimizedStorage.get()); 106*04eeddc0SDimitry Andric return EntryRef(/*Minimized=*/true, Filename, Entry); 107a7dea167SDimitry Andric } 108a7dea167SDimitry Andric 1090eae32dcSDimitry Andric DependencyScanningFilesystemSharedCache:: 1100eae32dcSDimitry Andric DependencyScanningFilesystemSharedCache() { 111a7dea167SDimitry Andric // This heuristic was chosen using a empirical testing on a 112a7dea167SDimitry Andric // reasonably high core machine (iMacPro 18 cores / 36 threads). The cache 113a7dea167SDimitry Andric // sharding gives a performance edge by reducing the lock contention. 114a7dea167SDimitry Andric // FIXME: A better heuristic might also consider the OS to account for 115a7dea167SDimitry Andric // the different cost of lock contention on different OSes. 1165ffd83dbSDimitry Andric NumShards = 1175ffd83dbSDimitry Andric std::max(2u, llvm::hardware_concurrency().compute_thread_count() / 4); 118a7dea167SDimitry Andric CacheShards = std::make_unique<CacheShard[]>(NumShards); 119a7dea167SDimitry Andric } 120a7dea167SDimitry Andric 121*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard & 122*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::getShardForFilename( 123*04eeddc0SDimitry Andric StringRef Filename) const { 124*04eeddc0SDimitry Andric return CacheShards[llvm::hash_value(Filename) % NumShards]; 125*04eeddc0SDimitry Andric } 126*04eeddc0SDimitry Andric 127*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard & 128*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::getShardForUID( 129*04eeddc0SDimitry Andric llvm::sys::fs::UniqueID UID) const { 130*04eeddc0SDimitry Andric auto Hash = llvm::hash_combine(UID.getDevice(), UID.getFile()); 131*04eeddc0SDimitry Andric return CacheShards[Hash % NumShards]; 132*04eeddc0SDimitry Andric } 133*04eeddc0SDimitry Andric 134*04eeddc0SDimitry Andric const CachedFileSystemEntry * 135*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename( 136*04eeddc0SDimitry Andric StringRef Filename) const { 137*04eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 138*04eeddc0SDimitry Andric auto It = EntriesByFilename.find(Filename); 139*04eeddc0SDimitry Andric return It == EntriesByFilename.end() ? nullptr : It->getValue(); 140*04eeddc0SDimitry Andric } 141*04eeddc0SDimitry Andric 142*04eeddc0SDimitry Andric const CachedFileSystemEntry * 143*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::findEntryByUID( 144*04eeddc0SDimitry Andric llvm::sys::fs::UniqueID UID) const { 145*04eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 146*04eeddc0SDimitry Andric auto It = EntriesByUID.find(UID); 147*04eeddc0SDimitry Andric return It == EntriesByUID.end() ? nullptr : It->getSecond(); 148*04eeddc0SDimitry Andric } 149*04eeddc0SDimitry Andric 150*04eeddc0SDimitry Andric const CachedFileSystemEntry & 151*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard:: 152*04eeddc0SDimitry Andric getOrEmplaceEntryForFilename(StringRef Filename, 153*04eeddc0SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> Stat) { 154*04eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 155*04eeddc0SDimitry Andric auto Insertion = EntriesByFilename.insert({Filename, nullptr}); 156*04eeddc0SDimitry Andric if (Insertion.second) 157*04eeddc0SDimitry Andric Insertion.first->second = 158*04eeddc0SDimitry Andric new (EntryStorage.Allocate()) CachedFileSystemEntry(std::move(Stat)); 159*04eeddc0SDimitry Andric return *Insertion.first->second; 160*04eeddc0SDimitry Andric } 161*04eeddc0SDimitry Andric 162*04eeddc0SDimitry Andric const CachedFileSystemEntry & 163*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard::getOrEmplaceEntryForUID( 164*04eeddc0SDimitry Andric llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat, 165*04eeddc0SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Contents) { 166*04eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 167*04eeddc0SDimitry Andric auto Insertion = EntriesByUID.insert({UID, nullptr}); 168*04eeddc0SDimitry Andric if (Insertion.second) { 169*04eeddc0SDimitry Andric CachedFileContents *StoredContents = nullptr; 170*04eeddc0SDimitry Andric if (Contents) 171*04eeddc0SDimitry Andric StoredContents = new (ContentsStorage.Allocate()) 172*04eeddc0SDimitry Andric CachedFileContents(std::move(Contents)); 173*04eeddc0SDimitry Andric Insertion.first->second = new (EntryStorage.Allocate()) 174*04eeddc0SDimitry Andric CachedFileSystemEntry(std::move(Stat), StoredContents); 175*04eeddc0SDimitry Andric } 176*04eeddc0SDimitry Andric return *Insertion.first->second; 177*04eeddc0SDimitry Andric } 178*04eeddc0SDimitry Andric 179*04eeddc0SDimitry Andric const CachedFileSystemEntry & 180*04eeddc0SDimitry Andric DependencyScanningFilesystemSharedCache::CacheShard:: 181*04eeddc0SDimitry Andric getOrInsertEntryForFilename(StringRef Filename, 182*04eeddc0SDimitry Andric const CachedFileSystemEntry &Entry) { 183*04eeddc0SDimitry Andric std::lock_guard<std::mutex> LockGuard(CacheLock); 184*04eeddc0SDimitry Andric return *EntriesByFilename.insert({Filename, &Entry}).first->getValue(); 185a7dea167SDimitry Andric } 186a7dea167SDimitry Andric 187480093f4SDimitry Andric /// Whitelist file extensions that should be minimized, treating no extension as 188480093f4SDimitry Andric /// a source file that should be minimized. 189480093f4SDimitry Andric /// 190480093f4SDimitry Andric /// This is kinda hacky, it would be better if we knew what kind of file Clang 191480093f4SDimitry Andric /// was expecting instead. 1924824e7fdSDimitry Andric static bool shouldMinimizeBasedOnExtension(StringRef Filename) { 193480093f4SDimitry Andric StringRef Ext = llvm::sys::path::extension(Filename); 194480093f4SDimitry Andric if (Ext.empty()) 195480093f4SDimitry Andric return true; // C++ standard library 196480093f4SDimitry Andric return llvm::StringSwitch<bool>(Ext) 197480093f4SDimitry Andric .CasesLower(".c", ".cc", ".cpp", ".c++", ".cxx", true) 198480093f4SDimitry Andric .CasesLower(".h", ".hh", ".hpp", ".h++", ".hxx", true) 199480093f4SDimitry Andric .CasesLower(".m", ".mm", true) 200480093f4SDimitry Andric .CasesLower(".i", ".ii", ".mi", ".mmi", true) 201480093f4SDimitry Andric .CasesLower(".def", ".inc", true) 202480093f4SDimitry Andric .Default(false); 203480093f4SDimitry Andric } 204480093f4SDimitry Andric 205480093f4SDimitry Andric static bool shouldCacheStatFailures(StringRef Filename) { 206480093f4SDimitry Andric StringRef Ext = llvm::sys::path::extension(Filename); 207480093f4SDimitry Andric if (Ext.empty()) 208480093f4SDimitry Andric return false; // This may be the module cache directory. 2094824e7fdSDimitry Andric // Only cache stat failures on source files. 2104824e7fdSDimitry Andric return shouldMinimizeBasedOnExtension(Filename); 211480093f4SDimitry Andric } 212480093f4SDimitry Andric 2134824e7fdSDimitry Andric void DependencyScanningWorkerFilesystem::disableMinimization( 214*04eeddc0SDimitry Andric StringRef Filename) { 215*04eeddc0SDimitry Andric // Since we're not done setting up `NotToBeMinimized` yet, we need to disable 216*04eeddc0SDimitry Andric // minimization explicitly. 217*04eeddc0SDimitry Andric if (llvm::ErrorOr<EntryRef> Result = 218*04eeddc0SDimitry Andric getOrCreateFileSystemEntry(Filename, /*DisableMinimization=*/true)) 219*04eeddc0SDimitry Andric NotToBeMinimized.insert(Result->getStatus().getUniqueID()); 2204824e7fdSDimitry Andric } 2214824e7fdSDimitry Andric 222*04eeddc0SDimitry Andric bool DependencyScanningWorkerFilesystem::shouldMinimize( 223*04eeddc0SDimitry Andric StringRef Filename, llvm::sys::fs::UniqueID UID) { 224*04eeddc0SDimitry Andric return shouldMinimizeBasedOnExtension(Filename) && 225*04eeddc0SDimitry Andric !NotToBeMinimized.contains(UID); 2264824e7fdSDimitry Andric } 2274824e7fdSDimitry Andric 228*04eeddc0SDimitry Andric const CachedFileSystemEntry & 229*04eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID( 230*04eeddc0SDimitry Andric TentativeEntry TEntry) { 231*04eeddc0SDimitry Andric auto &Shard = SharedCache.getShardForUID(TEntry.Status.getUniqueID()); 232*04eeddc0SDimitry Andric return Shard.getOrEmplaceEntryForUID(TEntry.Status.getUniqueID(), 233*04eeddc0SDimitry Andric std::move(TEntry.Status), 234*04eeddc0SDimitry Andric std::move(TEntry.Contents)); 235*04eeddc0SDimitry Andric } 236*04eeddc0SDimitry Andric 237*04eeddc0SDimitry Andric const CachedFileSystemEntry * 238*04eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough( 239*04eeddc0SDimitry Andric StringRef Filename) { 240*04eeddc0SDimitry Andric if (const auto *Entry = LocalCache.findEntryByFilename(Filename)) 241*04eeddc0SDimitry Andric return Entry; 242*04eeddc0SDimitry Andric auto &Shard = SharedCache.getShardForFilename(Filename); 243*04eeddc0SDimitry Andric if (const auto *Entry = Shard.findEntryByFilename(Filename)) 244*04eeddc0SDimitry Andric return &LocalCache.insertEntryForFilename(Filename, *Entry); 245*04eeddc0SDimitry Andric return nullptr; 246*04eeddc0SDimitry Andric } 247*04eeddc0SDimitry Andric 248*04eeddc0SDimitry Andric llvm::ErrorOr<const CachedFileSystemEntry &> 249*04eeddc0SDimitry Andric DependencyScanningWorkerFilesystem::computeAndStoreResult(StringRef Filename) { 250*04eeddc0SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> Stat = getUnderlyingFS().status(Filename); 251*04eeddc0SDimitry Andric if (!Stat) { 252*04eeddc0SDimitry Andric if (!shouldCacheStatFailures(Filename)) 253*04eeddc0SDimitry Andric return Stat.getError(); 254*04eeddc0SDimitry Andric const auto &Entry = 255*04eeddc0SDimitry Andric getOrEmplaceSharedEntryForFilename(Filename, Stat.getError()); 256*04eeddc0SDimitry Andric return insertLocalEntryForFilename(Filename, Entry); 257*04eeddc0SDimitry Andric } 258*04eeddc0SDimitry Andric 259*04eeddc0SDimitry Andric if (const auto *Entry = findSharedEntryByUID(*Stat)) 260*04eeddc0SDimitry Andric return insertLocalEntryForFilename(Filename, *Entry); 261*04eeddc0SDimitry Andric 262*04eeddc0SDimitry Andric auto TEntry = 263*04eeddc0SDimitry Andric Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(Filename); 264*04eeddc0SDimitry Andric 265*04eeddc0SDimitry Andric const CachedFileSystemEntry *SharedEntry = [&]() { 266*04eeddc0SDimitry Andric if (TEntry) { 267*04eeddc0SDimitry Andric const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry)); 268*04eeddc0SDimitry Andric return &getOrInsertSharedEntryForFilename(Filename, UIDEntry); 269*04eeddc0SDimitry Andric } 270*04eeddc0SDimitry Andric return &getOrEmplaceSharedEntryForFilename(Filename, TEntry.getError()); 271*04eeddc0SDimitry Andric }(); 272*04eeddc0SDimitry Andric 273*04eeddc0SDimitry Andric return insertLocalEntryForFilename(Filename, *SharedEntry); 274fe6060f1SDimitry Andric } 275fe6060f1SDimitry Andric 2760eae32dcSDimitry Andric llvm::ErrorOr<EntryRef> 277a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry( 278*04eeddc0SDimitry Andric StringRef Filename, bool DisableMinimization) { 279*04eeddc0SDimitry Andric if (const auto *Entry = findEntryByFilenameWithWriteThrough(Filename)) 280*04eeddc0SDimitry Andric return minimizeIfNecessary(*Entry, Filename, DisableMinimization) 281*04eeddc0SDimitry Andric .unwrapError(); 282*04eeddc0SDimitry Andric auto MaybeEntry = computeAndStoreResult(Filename); 283*04eeddc0SDimitry Andric if (!MaybeEntry) 284*04eeddc0SDimitry Andric return MaybeEntry.getError(); 285*04eeddc0SDimitry Andric return minimizeIfNecessary(*MaybeEntry, Filename, DisableMinimization) 286*04eeddc0SDimitry Andric .unwrapError(); 287a7dea167SDimitry Andric } 288a7dea167SDimitry Andric 289a7dea167SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> 290a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::status(const Twine &Path) { 291a7dea167SDimitry Andric SmallString<256> OwnedFilename; 292a7dea167SDimitry Andric StringRef Filename = Path.toStringRef(OwnedFilename); 2930eae32dcSDimitry Andric 2940eae32dcSDimitry Andric llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename); 295a7dea167SDimitry Andric if (!Result) 296a7dea167SDimitry Andric return Result.getError(); 2970eae32dcSDimitry Andric return Result->getStatus(); 298a7dea167SDimitry Andric } 299a7dea167SDimitry Andric 300a7dea167SDimitry Andric namespace { 301a7dea167SDimitry Andric 302a7dea167SDimitry Andric /// The VFS that is used by clang consumes the \c CachedFileSystemEntry using 303a7dea167SDimitry Andric /// this subclass. 304a7dea167SDimitry Andric class MinimizedVFSFile final : public llvm::vfs::File { 305a7dea167SDimitry Andric public: 306a7dea167SDimitry Andric MinimizedVFSFile(std::unique_ptr<llvm::MemoryBuffer> Buffer, 307a7dea167SDimitry Andric llvm::vfs::Status Stat) 308a7dea167SDimitry Andric : Buffer(std::move(Buffer)), Stat(std::move(Stat)) {} 309a7dea167SDimitry Andric 310e8d8bef9SDimitry Andric static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> 3110eae32dcSDimitry Andric create(EntryRef Entry, 312e8d8bef9SDimitry Andric ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings); 313a7dea167SDimitry Andric 314e8d8bef9SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> status() override { return Stat; } 315a7dea167SDimitry Andric 316a7dea167SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> 317a7dea167SDimitry Andric getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, 318a7dea167SDimitry Andric bool IsVolatile) override { 319a7dea167SDimitry Andric return std::move(Buffer); 320a7dea167SDimitry Andric } 321a7dea167SDimitry Andric 322a7dea167SDimitry Andric std::error_code close() override { return {}; } 323a7dea167SDimitry Andric 324a7dea167SDimitry Andric private: 325a7dea167SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> Buffer; 326a7dea167SDimitry Andric llvm::vfs::Status Stat; 327a7dea167SDimitry Andric }; 328a7dea167SDimitry Andric 329e8d8bef9SDimitry Andric } // end anonymous namespace 330e8d8bef9SDimitry Andric 331e8d8bef9SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> MinimizedVFSFile::create( 3320eae32dcSDimitry Andric EntryRef Entry, ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings) { 333*04eeddc0SDimitry Andric assert(!Entry.isError() && "error"); 334*04eeddc0SDimitry Andric 3350eae32dcSDimitry Andric if (Entry.isDirectory()) 3360eae32dcSDimitry Andric return std::make_error_code(std::errc::is_a_directory); 3370eae32dcSDimitry Andric 338a7dea167SDimitry Andric auto Result = std::make_unique<MinimizedVFSFile>( 339*04eeddc0SDimitry Andric llvm::MemoryBuffer::getMemBuffer(Entry.getContents(), 340*04eeddc0SDimitry Andric Entry.getStatus().getName(), 341a7dea167SDimitry Andric /*RequiresNullTerminator=*/false), 342*04eeddc0SDimitry Andric Entry.getStatus()); 3430eae32dcSDimitry Andric 3440eae32dcSDimitry Andric const auto *EntrySkipMappings = Entry.getPPSkippedRangeMapping(); 3450eae32dcSDimitry Andric if (EntrySkipMappings && !EntrySkipMappings->empty() && PPSkipMappings) 3460eae32dcSDimitry Andric (*PPSkipMappings)[Result->Buffer->getBufferStart()] = EntrySkipMappings; 3470eae32dcSDimitry Andric 348a7dea167SDimitry Andric return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>( 349a7dea167SDimitry Andric std::unique_ptr<llvm::vfs::File>(std::move(Result))); 350a7dea167SDimitry Andric } 351a7dea167SDimitry Andric 352a7dea167SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> 353a7dea167SDimitry Andric DependencyScanningWorkerFilesystem::openFileForRead(const Twine &Path) { 354a7dea167SDimitry Andric SmallString<256> OwnedFilename; 355a7dea167SDimitry Andric StringRef Filename = Path.toStringRef(OwnedFilename); 356a7dea167SDimitry Andric 3570eae32dcSDimitry Andric llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename); 358a7dea167SDimitry Andric if (!Result) 359a7dea167SDimitry Andric return Result.getError(); 360e8d8bef9SDimitry Andric return MinimizedVFSFile::create(Result.get(), PPSkipMappings); 361a7dea167SDimitry Andric } 362