diff --git a/llvm/include/llvm/Support/MemoryBuffer.h b/llvm/include/llvm/Support/MemoryBuffer.h index 6385805eba1d71..ed975c86c125f8 100644 --- a/llvm/include/llvm/Support/MemoryBuffer.h +++ b/llvm/include/llvm/Support/MemoryBuffer.h @@ -17,6 +17,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/Alignment.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBufferRef.h" @@ -90,9 +91,13 @@ class MemoryBuffer { /// \param IsVolatile Set to true to indicate that the contents of the file /// can change outside the user's control, e.g. when libclang tries to parse /// while the user is editing/updating the file or if the file is on an NFS. + /// + /// \param Alignment Set to indicate that the buffer should be aligned to at + /// least the specified alignment. static ErrorOr> getFile(const Twine &Filename, bool IsText = false, - bool RequiresNullTerminator = true, bool IsVolatile = false); + bool RequiresNullTerminator = true, bool IsVolatile = false, + Optional Alignment = None); /// Read all of the specified file into a MemoryBuffer as a stream /// (i.e. until EOF reached). This is useful for special files that @@ -105,7 +110,8 @@ class MemoryBuffer { /// Since this is in the middle of a file, the buffer is not null terminated. static ErrorOr> getOpenFileSlice(sys::fs::file_t FD, const Twine &Filename, uint64_t MapSize, - int64_t Offset, bool IsVolatile = false); + int64_t Offset, bool IsVolatile = false, + Optional Alignment = None); /// Given an already-open file descriptor, read the file and return a /// MemoryBuffer. @@ -113,9 +119,13 @@ class MemoryBuffer { /// \param IsVolatile Set to true to indicate that the contents of the file /// can change outside the user's control, e.g. when libclang tries to parse /// while the user is editing/updating the file or if the file is on an NFS. + /// + /// \param Alignment Set to indicate that the buffer should be aligned to at + /// least the specified alignment. static ErrorOr> getOpenFile(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, - bool RequiresNullTerminator = true, bool IsVolatile = false); + bool RequiresNullTerminator = true, bool IsVolatile = false, + Optional Alignment = None); /// Open the specified memory range as a MemoryBuffer. Note that InputData /// must be null terminated if RequiresNullTerminator is true. @@ -138,12 +148,13 @@ class MemoryBuffer { /// is "-". static ErrorOr> getFileOrSTDIN(const Twine &Filename, bool IsText = false, - bool RequiresNullTerminator = true); + bool RequiresNullTerminator = true, + Optional Alignment = None); /// Map a subrange of the specified file as a MemoryBuffer. static ErrorOr> getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset, - bool IsVolatile = false); + bool IsVolatile = false, Optional Alignment = None); //===--------------------------------------------------------------------===// // Provided for performance analysis. @@ -188,18 +199,23 @@ class WritableMemoryBuffer : public MemoryBuffer { } static ErrorOr> - getFile(const Twine &Filename, bool IsVolatile = false); + getFile(const Twine &Filename, bool IsVolatile = false, + Optional Alignment = None); /// Map a subrange of the specified file as a WritableMemoryBuffer. static ErrorOr> getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset, - bool IsVolatile = false); + bool IsVolatile = false, Optional Alignment = None); /// Allocate a new MemoryBuffer of the specified size that is not initialized. /// Note that the caller should initialize the memory allocated by this /// method. The memory is owned by the MemoryBuffer object. + /// + /// \param Alignment Set to indicate that the buffer should be aligned to at + /// least the specified alignment. static std::unique_ptr - getNewUninitMemBuffer(size_t Size, const Twine &BufferName = ""); + getNewUninitMemBuffer(size_t Size, const Twine &BufferName = "", + Optional Alignment = None); /// Allocate a new zero-initialized MemoryBuffer of the specified size. Note /// that the caller need not initialize the memory allocated by this method. diff --git a/llvm/lib/Support/MemoryBuffer.cpp b/llvm/lib/Support/MemoryBuffer.cpp index 9872dfa78b261f..6bb046e9b3dbb3 100644 --- a/llvm/lib/Support/MemoryBuffer.cpp +++ b/llvm/lib/Support/MemoryBuffer.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" +#include "llvm/Support/Alignment.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" @@ -109,7 +110,8 @@ class MemoryBufferMem : public MB { template static ErrorOr> getFileAux(const Twine &Filename, uint64_t MapSize, uint64_t Offset, - bool IsText, bool RequiresNullTerminator, bool IsVolatile); + bool IsText, bool RequiresNullTerminator, bool IsVolatile, + Optional Alignment); std::unique_ptr MemoryBuffer::getMemBuffer(StringRef InputData, StringRef BufferName, @@ -144,21 +146,24 @@ MemoryBuffer::getMemBufferCopy(StringRef InputData, const Twine &BufferName) { ErrorOr> MemoryBuffer::getFileOrSTDIN(const Twine &Filename, bool IsText, - bool RequiresNullTerminator) { + bool RequiresNullTerminator, + Optional Alignment) { SmallString<256> NameBuf; StringRef NameRef = Filename.toStringRef(NameBuf); if (NameRef == "-") return getSTDIN(); return getFile(Filename, IsText, RequiresNullTerminator, - /*IsVolatile=*/false); + /*IsVolatile=*/false, Alignment); } ErrorOr> MemoryBuffer::getFileSlice(const Twine &FilePath, uint64_t MapSize, - uint64_t Offset, bool IsVolatile) { + uint64_t Offset, bool IsVolatile, + Optional Alignment) { return getFileAux(FilePath, MapSize, Offset, /*IsText=*/false, - /*RequiresNullTerminator=*/false, IsVolatile); + /*RequiresNullTerminator=*/false, IsVolatile, + Alignment); } //===----------------------------------------------------------------------===// @@ -237,58 +242,67 @@ getMemoryBufferForStream(sys::fs::file_t FD, const Twine &BufferName) { ErrorOr> MemoryBuffer::getFile(const Twine &Filename, bool IsText, - bool RequiresNullTerminator, bool IsVolatile) { + bool RequiresNullTerminator, bool IsVolatile, + Optional Alignment) { return getFileAux(Filename, /*MapSize=*/-1, /*Offset=*/0, - IsText, RequiresNullTerminator, IsVolatile); + IsText, RequiresNullTerminator, IsVolatile, + Alignment); } template static ErrorOr> getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator, - bool IsVolatile); + bool IsVolatile, Optional Alignment); template static ErrorOr> getFileAux(const Twine &Filename, uint64_t MapSize, uint64_t Offset, - bool IsText, bool RequiresNullTerminator, bool IsVolatile) { + bool IsText, bool RequiresNullTerminator, bool IsVolatile, + Optional Alignment) { Expected FDOrErr = sys::fs::openNativeFileForRead( Filename, IsText ? sys::fs::OF_TextWithCRLF : sys::fs::OF_None); if (!FDOrErr) return errorToErrorCode(FDOrErr.takeError()); sys::fs::file_t FD = *FDOrErr; auto Ret = getOpenFileImpl(FD, Filename, /*FileSize=*/-1, MapSize, Offset, - RequiresNullTerminator, IsVolatile); + RequiresNullTerminator, IsVolatile, Alignment); sys::fs::closeFile(FD); return Ret; } ErrorOr> -WritableMemoryBuffer::getFile(const Twine &Filename, bool IsVolatile) { +WritableMemoryBuffer::getFile(const Twine &Filename, bool IsVolatile, + Optional Alignment) { return getFileAux( Filename, /*MapSize=*/-1, /*Offset=*/0, /*IsText=*/false, - /*RequiresNullTerminator=*/false, IsVolatile); + /*RequiresNullTerminator=*/false, IsVolatile, Alignment); } ErrorOr> WritableMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize, - uint64_t Offset, bool IsVolatile) { + uint64_t Offset, bool IsVolatile, + Optional Alignment) { return getFileAux( Filename, MapSize, Offset, /*IsText=*/false, - /*RequiresNullTerminator=*/false, IsVolatile); + /*RequiresNullTerminator=*/false, IsVolatile, Alignment); } std::unique_ptr -WritableMemoryBuffer::getNewUninitMemBuffer(size_t Size, const Twine &BufferName) { +WritableMemoryBuffer::getNewUninitMemBuffer(size_t Size, + const Twine &BufferName, + Optional Alignment) { using MemBuffer = MemoryBufferMem; + + // Use 16-byte alignment if no alignment is specified. + Align BufAlign = Alignment.value_or(Align(16)); + // Allocate space for the MemoryBuffer, the data and the name. It is important // that MemoryBuffer and data are aligned so PointerIntPair works with them. - // TODO: Is 16-byte alignment enough? We copy small object files with large - // alignment expectations into this buffer. SmallString<256> NameBuf; StringRef NameRef = BufferName.toStringRef(NameBuf); - size_t AlignedStringLen = alignTo(sizeof(MemBuffer) + NameRef.size() + 1, 16); - size_t RealLen = AlignedStringLen + Size + 1; + size_t StringLen = sizeof(MemBuffer) + NameRef.size() + 1; + size_t RealLen = StringLen + Size + 1 + BufAlign.value(); if (RealLen <= Size) // Check for rollover. return nullptr; char *Mem = static_cast(operator new(RealLen, std::nothrow)); @@ -299,7 +313,7 @@ WritableMemoryBuffer::getNewUninitMemBuffer(size_t Size, const Twine &BufferName CopyStringRef(Mem + sizeof(MemBuffer), NameRef); // The buffer begins after the name and must be aligned. - char *Buf = Mem + AlignedStringLen; + char *Buf = (char *)alignAddr(Mem + StringLen, BufAlign); Buf[Size] = 0; // Null terminate buffer. auto *Ret = new (Mem) MemBuffer(StringRef(Buf, Size), true); @@ -427,7 +441,7 @@ template static ErrorOr> getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator, - bool IsVolatile) { + bool IsVolatile, Optional Alignment) { static int PageSize = sys::Process::getPageSizeEstimate(); // Default is to map the full file. @@ -469,7 +483,8 @@ getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, return EC; #endif - auto Buf = WritableMemoryBuffer::getNewUninitMemBuffer(MapSize, Filename); + auto Buf = + WritableMemoryBuffer::getNewUninitMemBuffer(MapSize, Filename, Alignment); if (!Buf) { // Failed to create a buffer. The only way it can fail is if // new(std::nothrow) returns 0. @@ -495,18 +510,21 @@ getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, } ErrorOr> -MemoryBuffer::getOpenFile(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, - bool RequiresNullTerminator, bool IsVolatile) { +MemoryBuffer::getOpenFile(sys::fs::file_t FD, const Twine &Filename, + uint64_t FileSize, bool RequiresNullTerminator, + bool IsVolatile, Optional Alignment) { return getOpenFileImpl(FD, Filename, FileSize, FileSize, 0, - RequiresNullTerminator, IsVolatile); + RequiresNullTerminator, IsVolatile, + Alignment); } ErrorOr> -MemoryBuffer::getOpenFileSlice(sys::fs::file_t FD, const Twine &Filename, uint64_t MapSize, - int64_t Offset, bool IsVolatile) { +MemoryBuffer::getOpenFileSlice(sys::fs::file_t FD, const Twine &Filename, + uint64_t MapSize, int64_t Offset, + bool IsVolatile, Optional Alignment) { assert(MapSize != uint64_t(-1)); return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false, - IsVolatile); + IsVolatile, Alignment); } ErrorOr> MemoryBuffer::getSTDIN() { diff --git a/llvm/unittests/Support/MemoryBufferTest.cpp b/llvm/unittests/Support/MemoryBufferTest.cpp index 423d8f76118115..d3969fb106d2b3 100644 --- a/llvm/unittests/Support/MemoryBufferTest.cpp +++ b/llvm/unittests/Support/MemoryBufferTest.cpp @@ -226,6 +226,22 @@ TEST_F(MemoryBufferTest, make_new) { EXPECT_EQ(nullptr, Five.get()); } +TEST_F(MemoryBufferTest, getNewAligned) { + auto CheckAlignment = [](size_t AlignmentValue) { + Align Alignment(AlignmentValue); + OwningBuffer AlignedBuffer = + WritableMemoryBuffer::getNewUninitMemBuffer(0, "", Alignment); + EXPECT_TRUE(isAddrAligned(Alignment, AlignedBuffer->getBufferStart())); + }; + + // Test allocation with different alignments. + CheckAlignment(16); + CheckAlignment(32); + CheckAlignment(64); + CheckAlignment(128); + CheckAlignment(256); +} + void MemoryBufferTest::testGetOpenFileSlice(bool Reopen) { // Test that MemoryBuffer::getOpenFile works properly when no null // terminator is requested and the size is large enough to trigger