diff options
author | Reid Kleckner <rnk@google.com> | 2019-07-10 00:34:13 +0000 |
---|---|---|
committer | Reid Kleckner <rnk@google.com> | 2019-07-10 00:34:13 +0000 |
commit | cc418a3af45adbe740f868a31bde6155083f0b9d (patch) | |
tree | d0b24abef24564f0f5a81d17df58ec768b2de356 /llvm/lib/Support | |
parent | 9c147bd40bc93376df274e8a5d51c69a55199044 (diff) | |
download | bcm5719-llvm-cc418a3af45adbe740f868a31bde6155083f0b9d.tar.gz bcm5719-llvm-cc418a3af45adbe740f868a31bde6155083f0b9d.zip |
[Support] Move llvm::MemoryBuffer to sys::fs::file_t
Summary:
On Windows, Posix integer file descriptors are a compatibility layer
over native file handles provided by the C runtime. There is a hard
limit on the maximum number of file descriptors that a process can open,
and the limit is 8192. LLD typically doesn't run into this limit because
it opens input files, maps them into memory, and then immediately closes
the file descriptor. This prevents it from running out of FDs.
For various reasons, I'd like to open handles to every input file and
keep them open during linking. That requires migrating MemoryBuffer over
to taking open native file handles instead of integer FDs.
Reviewers: aganea, Bigcheese
Reviewed By: aganea
Subscribers: smeenai, silvas, mehdi_amini, hiraditya, steven_wu, dexonsmith, dang, llvm-commits, zturner
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D63453
llvm-svn: 365588
Diffstat (limited to 'llvm/lib/Support')
-rw-r--r-- | llvm/lib/Support/FileOutputBuffer.cpp | 3 | ||||
-rw-r--r-- | llvm/lib/Support/MemoryBuffer.cpp | 87 | ||||
-rw-r--r-- | llvm/lib/Support/Unix/Path.inc | 49 | ||||
-rw-r--r-- | llvm/lib/Support/VirtualFileSystem.cpp | 27 | ||||
-rw-r--r-- | llvm/lib/Support/Windows/Path.inc | 82 |
5 files changed, 170 insertions, 78 deletions
diff --git a/llvm/lib/Support/FileOutputBuffer.cpp b/llvm/lib/Support/FileOutputBuffer.cpp index 19ab2e9917a..3d6b569f299 100644 --- a/llvm/lib/Support/FileOutputBuffer.cpp +++ b/llvm/lib/Support/FileOutputBuffer.cpp @@ -147,7 +147,8 @@ createOnDiskBuffer(StringRef Path, size_t Size, unsigned Mode) { // Mmap it. std::error_code EC; auto MappedFile = llvm::make_unique<fs::mapped_file_region>( - File.FD, fs::mapped_file_region::readwrite, Size, 0, EC); + fs::convertFDToNativeFile(File.FD), fs::mapped_file_region::readwrite, + Size, 0, EC); // mmap(2) can fail if the underlying filesystem does not support it. // If that happens, we fall back to in-memory buffer as the last resort. diff --git a/llvm/lib/Support/MemoryBuffer.cpp b/llvm/lib/Support/MemoryBuffer.cpp index 92e39e118b9..d0e5bb154c1 100644 --- a/llvm/lib/Support/MemoryBuffer.cpp +++ b/llvm/lib/Support/MemoryBuffer.cpp @@ -182,7 +182,7 @@ class MemoryBufferMMapFile : public MB { } public: - MemoryBufferMMapFile(bool RequiresNullTerminator, int FD, uint64_t Len, + MemoryBufferMMapFile(bool RequiresNullTerminator, sys::fs::file_t FD, uint64_t Len, uint64_t Offset, std::error_code &EC) : MFR(FD, MB::Mapmode, getLegalMapSize(Len, Offset), getLegalMapOffset(Offset), EC) { @@ -208,16 +208,16 @@ public: } static ErrorOr<std::unique_ptr<WritableMemoryBuffer>> -getMemoryBufferForStream(int FD, const Twine &BufferName) { +getMemoryBufferForStream(sys::fs::file_t FD, const Twine &BufferName) { const ssize_t ChunkSize = 4096*4; SmallString<ChunkSize> Buffer; - ssize_t ReadBytes; + size_t ReadBytes; // Read into Buffer until we hit EOF. do { Buffer.reserve(Buffer.size() + ChunkSize); - ReadBytes = sys::RetryAfterSignal(-1, ::read, FD, Buffer.end(), ChunkSize); - if (ReadBytes == -1) - return std::error_code(errno, std::generic_category()); + if (auto EC = sys::fs::readNativeFile( + FD, makeMutableArrayRef(Buffer.end(), ChunkSize), &ReadBytes)) + return EC; Buffer.set_size(Buffer.size() + ReadBytes); } while (ReadBytes != 0); @@ -234,7 +234,7 @@ MemoryBuffer::getFile(const Twine &Filename, int64_t FileSize, template <typename MB> static ErrorOr<std::unique_ptr<MB>> -getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize, +getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator, bool IsVolatile); @@ -242,15 +242,14 @@ template <typename MB> static ErrorOr<std::unique_ptr<MB>> getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize, uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile) { - int FD; - std::error_code EC = sys::fs::openFileForRead(Filename, FD, sys::fs::OF_None); - - if (EC) - return EC; - + Expected<sys::fs::file_t> FDOrErr = + sys::fs::openNativeFileForRead(Filename, sys::fs::OF_None); + if (!FDOrErr) + return errorToErrorCode(FDOrErr.takeError()); + sys::fs::file_t FD = *FDOrErr; auto Ret = getOpenFileImpl<MB>(FD, Filename, FileSize, MapSize, Offset, RequiresNullTerminator, IsVolatile); - close(FD); + sys::fs::closeFile(FD); return Ret; } @@ -304,7 +303,7 @@ WritableMemoryBuffer::getNewMemBuffer(size_t Size, const Twine &BufferName) { return SB; } -static bool shouldUseMmap(int FD, +static bool shouldUseMmap(sys::fs::file_t FD, size_t FileSize, size_t MapSize, off_t Offset, @@ -362,12 +361,11 @@ static bool shouldUseMmap(int FD, static ErrorOr<std::unique_ptr<WriteThroughMemoryBuffer>> getReadWriteFile(const Twine &Filename, uint64_t FileSize, uint64_t MapSize, uint64_t Offset) { - int FD; - std::error_code EC = sys::fs::openFileForReadWrite( - Filename, FD, sys::fs::CD_OpenExisting, sys::fs::OF_None); - - if (EC) - return EC; + Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForReadWrite( + Filename, sys::fs::CD_OpenExisting, sys::fs::OF_None); + if (!FDOrErr) + return errorToErrorCode(FDOrErr.takeError()); + sys::fs::file_t FD = *FDOrErr; // Default is to map the full file. if (MapSize == uint64_t(-1)) { @@ -391,6 +389,7 @@ getReadWriteFile(const Twine &Filename, uint64_t FileSize, uint64_t MapSize, MapSize = FileSize; } + std::error_code EC; std::unique_ptr<WriteThroughMemoryBuffer> Result( new (NamedBufferAlloc(Filename)) MemoryBufferMMapFile<WriteThroughMemoryBuffer>(false, FD, MapSize, @@ -414,7 +413,7 @@ WriteThroughMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize, template <typename MB> static ErrorOr<std::unique_ptr<MB>> -getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize, +getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator, bool IsVolatile) { static int PageSize = sys::Process::getPageSizeEstimate(); @@ -459,45 +458,20 @@ getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize, return make_error_code(errc::not_enough_memory); } - char *BufPtr = Buf.get()->getBufferStart(); - - size_t BytesLeft = MapSize; -#ifndef HAVE_PREAD - if (lseek(FD, Offset, SEEK_SET) == -1) - return std::error_code(errno, std::generic_category()); -#endif - - while (BytesLeft) { -#ifdef HAVE_PREAD - ssize_t NumRead = sys::RetryAfterSignal(-1, ::pread, FD, BufPtr, BytesLeft, - MapSize - BytesLeft + Offset); -#else - ssize_t NumRead = sys::RetryAfterSignal(-1, ::read, FD, BufPtr, BytesLeft); -#endif - if (NumRead == -1) { - // Error while reading. - return std::error_code(errno, std::generic_category()); - } - if (NumRead == 0) { - memset(BufPtr, 0, BytesLeft); // zero-initialize rest of the buffer. - break; - } - BytesLeft -= NumRead; - BufPtr += NumRead; - } + sys::fs::readNativeFileSlice(FD, Buf->getBuffer(), Offset); return std::move(Buf); } ErrorOr<std::unique_ptr<MemoryBuffer>> -MemoryBuffer::getOpenFile(int FD, const Twine &Filename, uint64_t FileSize, +MemoryBuffer::getOpenFile(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) { return getOpenFileImpl<MemoryBuffer>(FD, Filename, FileSize, FileSize, 0, RequiresNullTerminator, IsVolatile); } ErrorOr<std::unique_ptr<MemoryBuffer>> -MemoryBuffer::getOpenFileSlice(int FD, const Twine &Filename, uint64_t MapSize, +MemoryBuffer::getOpenFileSlice(sys::fs::file_t FD, const Twine &Filename, uint64_t MapSize, int64_t Offset, bool IsVolatile) { assert(MapSize != uint64_t(-1)); return getOpenFileImpl<MemoryBuffer>(FD, Filename, -1, MapSize, Offset, false, @@ -511,18 +485,19 @@ ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getSTDIN() { // fallback if it fails. sys::ChangeStdinToBinary(); - return getMemoryBufferForStream(0, "<stdin>"); + return getMemoryBufferForStream(sys::fs::getStdinHandle(), "<stdin>"); } ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getFileAsStream(const Twine &Filename) { - int FD; - std::error_code EC = sys::fs::openFileForRead(Filename, FD, sys::fs::OF_None); - if (EC) - return EC; + Expected<sys::fs::file_t> FDOrErr = + sys::fs::openNativeFileForRead(Filename, sys::fs::OF_None); + if (!FDOrErr) + return errorToErrorCode(FDOrErr.takeError()); + sys::fs::file_t FD = *FDOrErr; ErrorOr<std::unique_ptr<MemoryBuffer>> Ret = getMemoryBufferForStream(FD, Filename); - close(FD); + sys::fs::closeFile(FD); return Ret; } diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index c64c0df19ad..58c15023bab 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -990,9 +990,54 @@ Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags, return ResultFD; } -void closeFile(file_t &F) { - ::close(F); +file_t getStdinHandle() { return 0; } +file_t getStdoutHandle() { return 1; } +file_t getStderrHandle() { return 2; } + +std::error_code readNativeFile(file_t FD, MutableArrayRef<char> Buf, + size_t *BytesRead) { + *BytesRead = sys::RetryAfterSignal(-1, ::read, FD, Buf.data(), Buf.size()); + if (ssize_t(*BytesRead) == -1) + return std::error_code(errno, std::generic_category()); + return std::error_code(); +} + +std::error_code readNativeFileSlice(file_t FD, MutableArrayRef<char> Buf, + size_t Offset) { + char *BufPtr = Buf.data(); + size_t BytesLeft = Buf.size(); + +#ifndef HAVE_PREAD + // If we don't have pread, seek to Offset. + if (lseek(FD, Offset, SEEK_SET) == -1) + return std::error_code(errno, std::generic_category()); +#endif + + while (BytesLeft) { +#ifdef HAVE_PREAD + ssize_t NumRead = sys::RetryAfterSignal(-1, ::pread, FD, BufPtr, BytesLeft, + Buf.size() - BytesLeft + Offset); +#else + ssize_t NumRead = sys::RetryAfterSignal(-1, ::read, FD, BufPtr, BytesLeft); +#endif + if (NumRead == -1) { + // Error while reading. + return std::error_code(errno, std::generic_category()); + } + if (NumRead == 0) { + memset(BufPtr, 0, BytesLeft); // zero-initialize rest of the buffer. + break; + } + BytesLeft -= NumRead; + BufPtr += NumRead; + } + return std::error_code(); +} + +std::error_code closeFile(file_t &F) { + file_t TmpF = F; F = kInvalidFile; + return Process::SafelyCloseFileDescriptor(TmpF); } template <typename T> diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp index 48ce31491a8..5d3480e9714 100644 --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -56,8 +56,10 @@ using namespace llvm; using namespace llvm::vfs; +using llvm::sys::fs::file_t; using llvm::sys::fs::file_status; using llvm::sys::fs::file_type; +using llvm::sys::fs::kInvalidFile; using llvm::sys::fs::perms; using llvm::sys::fs::UniqueID; @@ -170,15 +172,15 @@ namespace { class RealFile : public File { friend class RealFileSystem; - int FD; + file_t FD; Status S; std::string RealName; - RealFile(int FD, StringRef NewName, StringRef NewRealPathName) + RealFile(file_t FD, StringRef NewName, StringRef NewRealPathName) : FD(FD), S(NewName, {}, {}, {}, {}, {}, llvm::sys::fs::file_type::status_error, {}), RealName(NewRealPathName.str()) { - assert(FD >= 0 && "Invalid or inactive file descriptor"); + assert(FD != kInvalidFile && "Invalid or inactive file descriptor"); } public: @@ -198,7 +200,7 @@ public: RealFile::~RealFile() { close(); } ErrorOr<Status> RealFile::status() { - assert(FD != -1 && "cannot stat closed file"); + assert(FD != kInvalidFile && "cannot stat closed file"); if (!S.isStatusKnown()) { file_status RealStatus; if (std::error_code EC = sys::fs::status(FD, RealStatus)) @@ -215,14 +217,14 @@ ErrorOr<std::string> RealFile::getName() { ErrorOr<std::unique_ptr<MemoryBuffer>> RealFile::getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) { - assert(FD != -1 && "cannot get buffer for closed file"); + assert(FD != kInvalidFile && "cannot get buffer for closed file"); return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator, IsVolatile); } std::error_code RealFile::close() { - std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD); - FD = -1; + std::error_code EC = sys::fs::closeFile(FD); + FD = kInvalidFile; return EC; } @@ -293,12 +295,13 @@ ErrorOr<Status> RealFileSystem::status(const Twine &Path) { ErrorOr<std::unique_ptr<File>> RealFileSystem::openFileForRead(const Twine &Name) { - int FD; SmallString<256> RealName, Storage; - if (std::error_code EC = sys::fs::openFileForRead( - adjustPath(Name, Storage), FD, sys::fs::OF_None, &RealName)) - return EC; - return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str())); + Expected<file_t> FDOrErr = sys::fs::openNativeFileForRead( + adjustPath(Name, Storage), sys::fs::OF_None, &RealName); + if (!FDOrErr) + return errorToErrorCode(FDOrErr.takeError()); + return std::unique_ptr<File>( + new RealFile(*FDOrErr, Name.str(), RealName.str())); } llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const { diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index 9adda233c02..045d0b9ed58 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -734,6 +734,10 @@ std::error_code status(int FD, file_status &Result) { return getStatus(FileHandle, Result); } +std::error_code status(file_t FileHandle, file_status &Result) { + return getStatus(FileHandle, Result); +} + unsigned getUmask() { return 0; } @@ -780,10 +784,9 @@ std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime, return std::error_code(); } -std::error_code mapped_file_region::init(int FD, uint64_t Offset, - mapmode Mode) { +std::error_code mapped_file_region::init(sys::fs::file_t OrigFileHandle, + uint64_t Offset, mapmode Mode) { this->Mode = Mode; - HANDLE OrigFileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); if (OrigFileHandle == INVALID_HANDLE_VALUE) return make_error_code(errc::bad_file_descriptor); @@ -850,8 +853,9 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset, return std::error_code(); } -mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length, - uint64_t offset, std::error_code &ec) +mapped_file_region::mapped_file_region(sys::fs::file_t fd, mapmode mode, + size_t length, uint64_t offset, + std::error_code &ec) : Size(length), Mapping() { ec = init(fd, offset, mode); if (ec) @@ -1201,9 +1205,73 @@ Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags, return Result; } -void closeFile(file_t &F) { - ::CloseHandle(F); +file_t convertFDToNativeFile(int FD) { + return reinterpret_cast<HANDLE>(::_get_osfhandle(FD)); +} + +file_t getStdinHandle() { return ::GetStdHandle(STD_INPUT_HANDLE); } +file_t getStdoutHandle() { return ::GetStdHandle(STD_OUTPUT_HANDLE); } +file_t getStderrHandle() { return ::GetStdHandle(STD_ERROR_HANDLE); } + +std::error_code readNativeFileImpl(file_t FileHandle, char *BufPtr, size_t BytesToRead, + size_t *BytesRead, OVERLAPPED *Overlap) { + // ReadFile can only read 2GB at a time. The caller should check the number of + // bytes and read in a loop until termination. + DWORD BytesToRead32 = + std::min(size_t(std::numeric_limits<DWORD>::max()), BytesToRead); + DWORD BytesRead32 = 0; + bool Success = + ::ReadFile(FileHandle, BufPtr, BytesToRead32, &BytesRead32, Overlap); + *BytesRead = BytesRead32; + if (!Success) { + DWORD Err = ::GetLastError(); + // Pipe EOF is not an error. + if (Err == ERROR_BROKEN_PIPE) + return std::error_code(); + return mapWindowsError(Err); + } + return std::error_code(); +} + +std::error_code readNativeFile(file_t FileHandle, MutableArrayRef<char> Buf, + size_t *BytesRead) { + return readNativeFileImpl(FileHandle, Buf.data(), Buf.size(), BytesRead, + /*Overlap=*/nullptr); +} + +std::error_code readNativeFileSlice(file_t FileHandle, + MutableArrayRef<char> Buf, size_t Offset) { + char *BufPtr = Buf.data(); + size_t BytesLeft = Buf.size(); + + while (BytesLeft) { + uint64_t CurOff = Buf.size() - BytesLeft + Offset; + OVERLAPPED Overlapped = {}; + Overlapped.Offset = uint32_t(CurOff); + Overlapped.OffsetHigh = uint32_t(uint64_t(CurOff) >> 32); + + size_t BytesRead = 0; + if (auto EC = readNativeFileImpl(FileHandle, BufPtr, BytesLeft, &BytesRead, + &Overlapped)) + return EC; + + // Once we reach EOF, zero the remaining bytes in the buffer. + if (BytesRead == 0) { + memset(BufPtr, 0, BytesLeft); + break; + } + BytesLeft -= BytesRead; + BufPtr += BytesRead; + } + return std::error_code(); +} + +std::error_code closeFile(file_t &F) { + file_t TmpF = F; F = kInvalidFile; + if (!::CloseHandle(TmpF)) + return mapWindowsError(::GetLastError()); + return std::error_code(); } std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { |