From f7fbffe7d4ec02645188b3ff0b883e7d33eaf5bc Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 4 Jun 2025 11:45:43 -0700 Subject: [PATCH] Use `__cpp_aligned_new` instead of hand-rolling the implementation (#11293) Summary: Pull Request resolved: https://github.com/pytorch/executorch/pull/11293 The current feature detection seems to be incorrect, resulting in fallback to the manual implementation, which in turn returns untagged pointers under hwasan. Use new expressions instead to delegate the work to the C++ compiler. Reviewed By: lucylq Differential Revision: D75806635 --- extension/data_loader/file_data_loader.cpp | 39 +++++--- extension/data_loader/file_data_loader.h | 6 +- .../file_descriptor_data_loader.cpp | 37 ++++++-- .../data_loader/file_descriptor_data_loader.h | 6 +- runtime/platform/compiler.h | 95 ------------------- 5 files changed, 62 insertions(+), 121 deletions(-) diff --git a/extension/data_loader/file_data_loader.cpp b/extension/data_loader/file_data_loader.cpp index e9922eb8323..92c4cd61eea 100644 --- a/extension/data_loader/file_data_loader.cpp +++ b/extension/data_loader/file_data_loader.cpp @@ -112,17 +112,29 @@ Result FileDataLoader::from( } namespace { + +inline void* et_aligned_alloc(size_t size, std::align_val_t alignment) { + return ::operator new(size, alignment); +} + +inline void et_aligned_free(void* ptr, std::align_val_t alignment) { + return ::operator delete(ptr, alignment); +} + /** * FreeableBuffer::FreeFn-compatible callback. * - * `context` is the original buffer pointer. It is allocated with - * ET_ALIGNED_ALLOC, and must be freed with ET_ALIGNED_FREE. + * `data` is the original buffer pointer. + * `context` is the original alignment. * - * `data` and `size` are unused. + * `size` is unused. */ void FreeSegment(void* context, void* data, ET_UNUSED size_t size) { - ET_ALIGNED_FREE(context); + et_aligned_free( + data, + static_cast(reinterpret_cast(context))); } + } // namespace Result FileDataLoader::load( @@ -149,26 +161,31 @@ Result FileDataLoader::load( } // Allocate memory for the FreeableBuffer. - void* aligned_buffer = ET_ALIGNED_ALLOC(alignment_, size); + void* aligned_buffer = et_aligned_alloc(size, alignment_); if (aligned_buffer == nullptr) { ET_LOG( Error, - "Reading from %s at offset %zu: ET_ALIGNED_ALLOC(%zd, %zd) failed", + "Reading from %s at offset %zu: et_aligned_alloc(%zu, %zu) failed", file_name_, offset, - alignment_, - size); + size, + static_cast(alignment_)); return Error::MemoryAllocationFailed; } auto err = load_into(offset, size, segment_info, aligned_buffer); if (err != Error::Ok) { - ET_ALIGNED_FREE(aligned_buffer); + et_aligned_free(aligned_buffer, alignment_); return err; } - // Pass the aligned_buffer pointer as context to FreeSegment. - return FreeableBuffer(aligned_buffer, size, FreeSegment, aligned_buffer); + // Pass the alignment as context to FreeSegment. + return FreeableBuffer( + aligned_buffer, + size, + FreeSegment, + // NOLINTNEXTLINE(performance-no-int-to-ptr) + reinterpret_cast(static_cast(alignment_))); } Result FileDataLoader::size() const { diff --git a/extension/data_loader/file_data_loader.h b/extension/data_loader/file_data_loader.h index 7cf2a92c4ad..596c005b714 100644 --- a/extension/data_loader/file_data_loader.h +++ b/extension/data_loader/file_data_loader.h @@ -58,7 +58,7 @@ class FileDataLoader final : public executorch::runtime::DataLoader { fd_(rhs.fd_) { const_cast(rhs.file_name_) = nullptr; const_cast(rhs.file_size_) = 0; - const_cast(rhs.alignment_) = 0; + const_cast(rhs.alignment_) = {}; const_cast(rhs.fd_) = -1; } @@ -86,7 +86,7 @@ class FileDataLoader final : public executorch::runtime::DataLoader { const char* file_name) : file_name_(file_name), file_size_(file_size), - alignment_(alignment), + alignment_{alignment}, fd_(fd) {} // Not safely copyable. @@ -96,7 +96,7 @@ class FileDataLoader final : public executorch::runtime::DataLoader { const char* const file_name_; // Owned by the instance. const size_t file_size_; - const size_t alignment_; + const std::align_val_t alignment_; const int fd_; // Owned by the instance. }; diff --git a/extension/data_loader/file_descriptor_data_loader.cpp b/extension/data_loader/file_descriptor_data_loader.cpp index d946b506905..a57f2ce3640 100644 --- a/extension/data_loader/file_descriptor_data_loader.cpp +++ b/extension/data_loader/file_descriptor_data_loader.cpp @@ -123,17 +123,29 @@ FileDescriptorDataLoader::fromFileDescriptorUri( } namespace { + +inline void* et_aligned_alloc(size_t size, std::align_val_t alignment) { + return ::operator new(size, alignment); +} + +inline void et_aligned_free(void* ptr, std::align_val_t alignment) { + return ::operator delete(ptr, alignment); +} + /** * FreeableBuffer::FreeFn-compatible callback. * - * `context` is the original buffer pointer. It is allocated with - * ET_ALIGNED_ALLOC, and must be freed with ET_ALIGNED_FREE. + * `data` is the original buffer pointer. + * `context` is the original alignment. * - * `data` and `size` are unused. + * `size` is unused. */ void FreeSegment(void* context, void* data, ET_UNUSED size_t size) { - ET_ALIGNED_FREE(context); + et_aligned_free( + data, + static_cast(reinterpret_cast(context))); } + } // namespace Result FileDescriptorDataLoader::load( @@ -160,24 +172,31 @@ Result FileDescriptorDataLoader::load( } // Allocate memory for the FreeableBuffer. - void* aligned_buffer = ET_ALIGNED_ALLOC(alignment_, size); + void* aligned_buffer = et_aligned_alloc(size, alignment_); if (aligned_buffer == nullptr) { ET_LOG( Error, - "Reading from %s at offset %zu: ET_ALIGNED_ALLOC(%zd) failed", + "Reading from %s at offset %zu: et_aligned_alloc(%zu, %zu) failed", file_descriptor_uri_, offset, - size); + size, + static_cast(alignment_)); return Error::MemoryAllocationFailed; } auto err = load_into(offset, size, segment_info, aligned_buffer); if (err != Error::Ok) { - ET_ALIGNED_FREE(aligned_buffer); + et_aligned_free(aligned_buffer, alignment_); return err; } - return FreeableBuffer(aligned_buffer, size, FreeSegment, aligned_buffer); + // Pass the alignment as context to FreeSegment. + return FreeableBuffer( + aligned_buffer, + size, + FreeSegment, + // NOLINTNEXTLINE(performance-no-int-to-ptr) + reinterpret_cast(static_cast(alignment_))); } Result FileDescriptorDataLoader::size() const { diff --git a/extension/data_loader/file_descriptor_data_loader.h b/extension/data_loader/file_descriptor_data_loader.h index 6f51f0f7a62..eff64835c84 100644 --- a/extension/data_loader/file_descriptor_data_loader.h +++ b/extension/data_loader/file_descriptor_data_loader.h @@ -56,7 +56,7 @@ class FileDescriptorDataLoader final : public executorch::runtime::DataLoader { fd_(rhs.fd_) { const_cast(rhs.file_descriptor_uri_) = nullptr; const_cast(rhs.file_size_) = 0; - const_cast(rhs.alignment_) = 0; + const_cast(rhs.alignment_) = {}; const_cast(rhs.fd_) = -1; } @@ -84,7 +84,7 @@ class FileDescriptorDataLoader final : public executorch::runtime::DataLoader { const char* file_descriptor_uri) : file_descriptor_uri_(file_descriptor_uri), file_size_(file_size), - alignment_(alignment), + alignment_{alignment}, fd_(fd) {} // Not safely copyable. @@ -94,7 +94,7 @@ class FileDescriptorDataLoader final : public executorch::runtime::DataLoader { const char* const file_descriptor_uri_; // Owned by the instance. const size_t file_size_; - const size_t alignment_; + const std::align_val_t alignment_; const int fd_; // Owned by the instance. }; diff --git a/runtime/platform/compiler.h b/runtime/platform/compiler.h index da7e0988a62..7467d5c1e04 100644 --- a/runtime/platform/compiler.h +++ b/runtime/platform/compiler.h @@ -171,101 +171,6 @@ using ssize_t = ptrdiff_t; #endif -/** - * Platform-specific aligned memory allocation and deallocation. - * - * Usage: - * void* ptr = ET_ALIGNED_ALLOC(alignment, size); - * // use ptr... - * ET_ALIGNED_FREE(ptr); - * - * Note: alignment must be a power of 2 and size must be an integral multiple of - * alignment. - */ -#if defined(_MSC_VER) -#include -#define ET_ALIGNED_ALLOC(alignment, size) \ - _aligned_malloc(((size + alignment - 1) & ~(alignment - 1)), (alignment)) -#define ET_ALIGNED_FREE(ptr) _aligned_free(ptr) -#elif defined(__APPLE__) -#include // For posix_memalign and free -inline void* et_apple_aligned_alloc(size_t alignment, size_t size) { - void* ptr = nullptr; - // The address of the allocated memory must be a multiple of sizeof(void*). - if (alignment < sizeof(void*)) { - alignment = sizeof(void*); - } - if (posix_memalign( - &ptr, alignment, (size + alignment - 1) & ~(alignment - 1)) != 0) { - return nullptr; - } - return ptr; -} -#define ET_ALIGNED_ALLOC(alignment, size) \ - et_apple_aligned_alloc((alignment), (size)) -#define ET_ALIGNED_FREE(ptr) free(ptr) -#elif __has_builtin(__builtin_aligned_alloc) || defined(_ISOC11_SOURCE) -// Linux and posix systems that support aligned_alloc and are >= C++17. -#include -#define ET_ALIGNED_ALLOC(alignment, size) \ - ::aligned_alloc(alignment, (size + alignment - 1) & ~(alignment - 1)) -#define ET_ALIGNED_FREE(ptr) free(ptr) -#else -// If the platform doesn't support aligned_alloc, fallback to malloc. -#include -#include -inline void* et_aligned_malloc(size_t alignment, size_t size) { - // Place to store the offset to the original pointer. - size_t offset_size = sizeof(uint16_t); - - // Malloc extra space for offset + alignment. - size_t alloc_size = size + offset_size + alignment - 1; - void* ptr = std::malloc(alloc_size); - - if (ptr == nullptr) { - // Malloc failed. - return nullptr; - } - - uintptr_t addr = reinterpret_cast(ptr); - // Align the address past addr + offset_size bytes. - // This provides space to store the offset before the aligned pointer. - addr = addr + offset_size; - uintptr_t aligned_ptr = (addr + alignment - 1) & ~(alignment - 1); - - // Check that alignment didn't overflow the buffer. - if (reinterpret_cast(aligned_ptr) + size > - reinterpret_cast(ptr) + alloc_size) { - std::free(ptr); - return nullptr; - } - - // Store the offset to the original pointer. - // Used to free the original allocated buffer. - *(reinterpret_cast(aligned_ptr) - 1) = - (uint16_t)(reinterpret_cast(aligned_ptr) - - reinterpret_cast(ptr)); - - return reinterpret_cast(aligned_ptr); -} - -inline void et_aligned_free(void* ptr) { - if (ptr == nullptr) { - return; - } - - // Get the original pointer using the offset. - uint16_t* original_ptr = reinterpret_cast( - reinterpret_cast(ptr) - - *(reinterpret_cast(ptr) - 1)); - std::free(original_ptr); -} - -#define ET_ALIGNED_ALLOC(alignment, size) et_aligned_malloc((alignment), (size)) -#define ET_ALIGNED_FREE(ptr) et_aligned_free(ptr) - -#endif - // DEPRECATED: Use the non-underscore-prefixed versions instead. // TODO(T199005537): Remove these once all users have stopped using them. #define __ET_DEPRECATED ET_DEPRECATED