diff --git a/clang/include/clang/Serialization/ModuleCache.h b/clang/include/clang/Serialization/ModuleCache.h index c6795c5dc358..a945135b4c42 100644 --- a/clang/include/clang/Serialization/ModuleCache.h +++ b/clang/include/clang/Serialization/ModuleCache.h @@ -12,9 +12,11 @@ #include "clang/Basic/LLVM.h" #include +#include namespace llvm { class AdvisoryLock; +class MemoryBuffer; } // namespace llvm namespace clang { @@ -52,7 +54,10 @@ public: virtual InMemoryModuleCache &getInMemoryModuleCache() = 0; virtual const InMemoryModuleCache &getInMemoryModuleCache() const = 0; - // TODO: Virtualize writing/reading PCM files, etc. + // TODO: Virtualize writing PCM files. + + virtual Expected> + read(StringRef FileName, off_t &Size, time_t &ModTime) = 0; virtual ~ModuleCache() = default; }; @@ -65,6 +70,10 @@ std::shared_ptr createCrossProcessModuleCache(); /// Shared implementation of `ModuleCache::maybePrune()`. void maybePruneImpl(StringRef Path, time_t PruneInterval, time_t PruneAfter); + +/// Shared implementation of `ModuleCache::read()`. +Expected> +readImpl(StringRef FileName, off_t &Size, time_t &ModTime); } // namespace clang #endif diff --git a/clang/lib/DependencyScanning/InProcessModuleCache.cpp b/clang/lib/DependencyScanning/InProcessModuleCache.cpp index cd7385c8f38c..9f46f32bf331 100644 --- a/clang/lib/DependencyScanning/InProcessModuleCache.cpp +++ b/clang/lib/DependencyScanning/InProcessModuleCache.cpp @@ -11,6 +11,8 @@ #include "clang/Serialization/InMemoryModuleCache.h" #include "llvm/Support/AdvisoryLock.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/IOSandbox.h" using namespace clang; using namespace dependencies; @@ -131,6 +133,16 @@ public: const InMemoryModuleCache &getInMemoryModuleCache() const override { return InMemory; } + + Expected> + read(StringRef FileName, off_t &Size, time_t &ModTime) override { + // This is a compiler-internal input/output, let's bypass the sandbox. + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + + // FIXME: This only needs to go to disk once per build, not in every + // compilation. Introduce in-memory cache. + return readImpl(FileName, Size, ModTime); + } }; } // namespace diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp index 658da6e3b714..5e618a3da08d 100644 --- a/clang/lib/Serialization/ModuleCache.cpp +++ b/clang/lib/Serialization/ModuleCache.cpp @@ -10,6 +10,7 @@ #include "clang/Serialization/InMemoryModuleCache.h" #include "clang/Serialization/ModuleFile.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/IOSandbox.h" #include "llvm/Support/LockFileManager.h" @@ -101,6 +102,25 @@ void clang::maybePruneImpl(StringRef Path, time_t PruneInterval, } } +Expected> +clang::readImpl(StringRef FileName, off_t &Size, time_t &ModTime) { + Expected FD = + llvm::sys::fs::openNativeFileForRead(FileName); + if (!FD) + return FD.takeError(); + llvm::sys::fs::file_status Status; + if (std::error_code EC = llvm::sys::fs::status(*FD, Status)) + return llvm::errorCodeToError(EC); + llvm::ErrorOr> Buf = + llvm::MemoryBuffer::getOpenFile(*FD, FileName, Status.getSize(), + /*RequiresNullTerminator=*/false); + if (!Buf) + return llvm::errorCodeToError(Buf.getError()); + Size = Status.getSize(); + ModTime = llvm::sys::toTimeT(Status.getLastModificationTime()); + return std::move(*Buf); +} + namespace { class CrossProcessModuleCache : public ModuleCache { InMemoryModuleCache InMemory; @@ -161,6 +181,14 @@ public: const InMemoryModuleCache &getInMemoryModuleCache() const override { return InMemory; } + + Expected> + read(StringRef FileName, off_t &Size, time_t &ModTime) override { + // This is a compiler-internal input/output, let's bypass the sandbox. + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + + return readImpl(FileName, Size, ModTime); + } }; } // namespace diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp index ed7b6cf67674..022e2ef42f63 100644 --- a/clang/lib/Serialization/ModuleManager.cpp +++ b/clang/lib/Serialization/ModuleManager.cpp @@ -28,6 +28,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator.h" #include "llvm/Support/DOTGraphTraits.h" +#include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/MemoryBuffer.h" @@ -188,33 +189,36 @@ ModuleManager::AddModuleResult ModuleManager::addModule( // import it earlier. return OutOfDate; } else { - OptionalFileEntryRef Entry = - expectedToOptional(FileName == StringRef("-") - ? FileMgr.getSTDIN() - : FileMgr.getFileRef(FileName, /*OpenFile=*/true, - /*CacheFailure=*/false)); - if (!Entry) { - ErrorStr = "module file not found"; - return Missing; - } + auto Buf = [&]() -> Expected> { + // Implicit modules live in the module cache. + if (FileName.getImplicitModuleSuffixLength()) + return ModCache.read(FileName, Size, ModTime); - // Get a buffer of the file and close the file descriptor when done. - // The file is volatile because in a parallel build we expect multiple - // compiler processes to use the same module file rebuilding it if needed. - // - // RequiresNullTerminator is false because module files don't need it, and - // this allows the file to still be mmapped. - auto Buf = FileMgr.getBufferForFile(*Entry, - /*IsVolatile=*/true, - /*RequiresNullTerminator=*/false); + // Explicit modules are treated as any other compiler input file, load + // them via FileManager. + Expected Entry = + FileName == StringRef("-") + ? FileMgr.getSTDIN() + : FileMgr.getFileRef(FileName, /*OpenFile=*/true, + /*CacheFailure=*/false); + if (!Entry) + return Entry.takeError(); + + Size = Entry->getSize(); + ModTime = Entry->getModificationTime(); + + // RequiresNullTerminator is false because module files don't need it, and + // this allows the file to still be mmapped. + return llvm::errorOrToExpected( + FileMgr.getBufferForFile(*Entry, /*IsVolatile=*/false, + /*RequiresNullTerminator=*/false)); + }(); if (!Buf) { - ErrorStr = Buf.getError().message(); + ErrorStr = llvm::toString(Buf.takeError()); return Missing; } - Size = Entry->getSize(); - ModTime = Entry->getModificationTime(); NewFileBuffer = std::move(*Buf); ModuleBuffer = NewFileBuffer.get(); }