From 2177f05a16891e5dc381ccfa56d36ce91418aa09 Mon Sep 17 00:00:00 2001 From: Phil Willoughby Date: Thu, 20 Jul 2017 05:28:49 -0700 Subject: [PATCH] Introduce folly::max_align_t and folly::max_align_v Summary: `folly::max_align_t` is a portable replacement for `std::max_align_t`. `folly::max_align_v` is a `constexpr size_t` with the same value as `alignof(folly::max_align_t)` 32-bit iOS environments have `alignof(std::max_align_t) == 4`, which is not correct. This is more correct in that I have not yet found a more-aligned basic type. Reviewed By: yfeldblum Differential Revision: D5442333 fbshipit-source-id: 0a93e48a730d65ef76e04f132b5976c93587b14c --- folly/CachelinePadded.h | 5 +- folly/MicroSpinLock.h | 9 +-- folly/Portability.h | 73 ++++++++++++++++++++- folly/concurrency/CacheLocality.cpp | 5 +- folly/concurrency/CacheLocality.h | 2 +- folly/experimental/hazptr/memory_resource.h | 10 +-- folly/io/IOBuf.cpp | 2 +- folly/test/CachelinePaddedTest.cpp | 6 +- folly/test/ThreadCachedArenaTest.cpp | 2 +- 9 files changed, 92 insertions(+), 22 deletions(-) diff --git a/folly/CachelinePadded.h b/folly/CachelinePadded.h index fe362a83..faaa1eeb 100644 --- a/folly/CachelinePadded.h +++ b/folly/CachelinePadded.h @@ -18,6 +18,7 @@ #include +#include #include namespace folly { @@ -32,8 +33,8 @@ namespace folly { template class CachelinePadded { static_assert( - alignof(T) < CacheLocality::kFalseSharingRange, - "CachelinePadded does not support types aligned >= a cache-line."); + alignof(T) <= folly::max_align_v, + "CachelinePadded does not support over-aligned types."); public: template diff --git a/folly/MicroSpinLock.h b/folly/MicroSpinLock.h index 737ac70e..ee2c4420 100644 --- a/folly/MicroSpinLock.h +++ b/folly/MicroSpinLock.h @@ -142,10 +142,11 @@ struct FOLLY_ALIGNED_MAX SpinLockArray { "Invalid size of PaddedSpinLock"); // Check if T can theoretically cross a cache line. - static_assert(alignof(std::max_align_t) > 0 && - FOLLY_CACHE_LINE_SIZE % alignof(std::max_align_t) == 0 && - sizeof(T) <= alignof(std::max_align_t), - "T can cross cache line boundaries"); + static_assert( + folly::max_align_v > 0 && + FOLLY_CACHE_LINE_SIZE % folly::max_align_v == 0 && + sizeof(T) <= folly::max_align_v, + "T can cross cache line boundaries"); char padding_[FOLLY_CACHE_LINE_SIZE]; std::array data_; diff --git a/folly/Portability.h b/folly/Portability.h index 053b5b16..d0c14dd7 100644 --- a/folly/Portability.h +++ b/folly/Portability.h @@ -19,6 +19,7 @@ #include #include +#include #include @@ -31,7 +32,75 @@ constexpr bool kHasUnalignedAccess = true; #else constexpr bool kHasUnalignedAccess = false; #endif -} + +namespace detail { + +template +struct integral_max + : std::integral_constant< + I, + (A > integral_max::value) ? A + : integral_max::value> { +}; + +template +struct integral_max : std::integral_constant {}; + +template +using max_alignment = integral_max; + +using max_basic_alignment = max_alignment< + std::max_align_t, + long double, + double, + float, + long long int, + long int, + int, + short int, + bool, + char, + char16_t, + char32_t, + wchar_t, + std::nullptr_t>; +} // namespace detail + +constexpr size_t max_align_v = detail::max_basic_alignment::value; + +// max_align_t is a type which is aligned at least as strictly as the +// most-aligned basic type (see the specification of std::max_align_t). This +// implementation exists because 32-bit iOS platforms have a broken +// std::max_align_t (see below). +// +// You should refer to this as `::folly::max_align_t` in portable code, even if +// you have `using namespace folly;` because C11 defines a global namespace +// `max_align_t` type. +// +// To be certain, we consider every non-void fundamental type specified by the +// standard. On most platforms `long double` would be enough, but iOS 32-bit +// has an 8-byte aligned `double` and `long long int` and a 4-byte aligned +// `long double`. +// +// So far we've covered locals and other non-allocated storage, but we also need +// confidence that allocated storage from `malloc`, `new`, etc will also be +// suitable for objects with this alignment reuirement. +// +// Apple document that their implementation of malloc will issue 16-byte +// granularity chunks for small allocations (large allocations are page-size +// granularity and page-aligned). We think that allocated storage will be +// suitable for these objects based on the following assumptions: +// +// 1. 16-byte granularity also means 16-byte aligned. +// 2. `new` and other allocators follow the `malloc` rules. +// +// We also have some anecdotal evidence: we don't see lots of misaligned-storage +// crashes on 32-bit iOS apps that use `double`. +// +// Apple's allocation reference: http://bit.ly/malloc-small +struct alignas(max_align_v) max_align_t {}; + +} // namespace folly // compiler specific attribute translation // msvc should come first, so if clang is in msvc mode it gets the right defines @@ -43,7 +112,7 @@ constexpr bool kHasUnalignedAccess = false; #else # error Cannot define FOLLY_ALIGNED on this platform #endif -#define FOLLY_ALIGNED_MAX FOLLY_ALIGNED(alignof(std::max_align_t)) +#define FOLLY_ALIGNED_MAX FOLLY_ALIGNED(::folly::max_align_v) // NOTE: this will only do checking in msvc with versions that support /analyze #if _MSC_VER diff --git a/folly/concurrency/CacheLocality.cpp b/folly/concurrency/CacheLocality.cpp index bb465d46..7f1253d1 100644 --- a/folly/concurrency/CacheLocality.cpp +++ b/folly/concurrency/CacheLocality.cpp @@ -257,9 +257,8 @@ void* SimpleAllocator::allocateHard() { // Install a pointer to ourselves as the allocator. *reinterpret_cast(mem_) = this; static_assert( - alignof(std::max_align_t) >= sizeof(SimpleAllocator*), - "alignment too small"); - mem_ += std::min(sz_, alignof(std::max_align_t)); + folly::max_align_v >= sizeof(SimpleAllocator*), "alignment too small"); + mem_ += std::min(sz_, folly::max_align_v); // New allocation. auto mem = mem_; diff --git a/folly/concurrency/CacheLocality.h b/folly/concurrency/CacheLocality.h index f2a39179..38f5557c 100644 --- a/folly/concurrency/CacheLocality.h +++ b/folly/concurrency/CacheLocality.h @@ -390,7 +390,7 @@ class SimpleAllocator { if (intptr_t(mem_) % 128 == 0) { // Avoid allocating pointers that may look like malloc // pointers. - mem_ += std::min(sz_, alignof(std::max_align_t)); + mem_ += std::min(sz_, folly::max_align_v); } if (mem_ && (mem_ + sz_ <= end_)) { auto mem = mem_; diff --git a/folly/experimental/hazptr/memory_resource.h b/folly/experimental/hazptr/memory_resource.h index 8a855685..bb049f87 100644 --- a/folly/experimental/hazptr/memory_resource.h +++ b/folly/experimental/hazptr/memory_resource.h @@ -22,7 +22,7 @@ /// std::pmr::memory_resource (C++17) as needed for developing a /// hazptr prototype. //////////////////////////////////////////////////////////////////////////////// -#include +#include #include namespace folly { @@ -33,11 +33,11 @@ class memory_resource { virtual ~memory_resource() = default; virtual void* allocate( const size_t bytes, - const size_t alignment = alignof(std::max_align_t)) = 0; + const size_t alignment = folly::max_align_v) = 0; virtual void deallocate( void* p, const size_t bytes, - const size_t alignment = alignof(std::max_align_t)) = 0; + const size_t alignment = folly::max_align_v) = 0; }; memory_resource* get_default_resource(); @@ -70,7 +70,7 @@ inline memory_resource* new_delete_resource() { public: void* allocate( const size_t bytes, - const size_t alignment = alignof(std::max_align_t)) override { + const size_t alignment = folly::max_align_v) override { (void)alignment; void* p = static_cast(new char[bytes]); DEBUG_PRINT(this << " " << p << " " << bytes); @@ -79,7 +79,7 @@ inline memory_resource* new_delete_resource() { void deallocate( void* p, const size_t bytes, - const size_t alignment = alignof(std::max_align_t)) override { + const size_t alignment = folly::max_align_v) override { (void)alignment; (void)bytes; DEBUG_PRINT(p << " " << bytes); diff --git a/folly/io/IOBuf.cpp b/folly/io/IOBuf.cpp index a71ff32e..3ca39aa2 100644 --- a/folly/io/IOBuf.cpp +++ b/folly/io/IOBuf.cpp @@ -114,7 +114,7 @@ struct IOBuf::HeapFullStorage { HeapStorage hs; SharedInfo shared; - std::max_align_t align; + folly::max_align_t align; }; IOBuf::SharedInfo::SharedInfo() diff --git a/folly/test/CachelinePaddedTest.cpp b/folly/test/CachelinePaddedTest.cpp index 11cefe33..9c125301 100644 --- a/folly/test/CachelinePaddedTest.cpp +++ b/folly/test/CachelinePaddedTest.cpp @@ -67,7 +67,7 @@ using CachelinePaddedTypes = ::testing::Types< SizedData, SizedData, // Mimic single basic types: - SizedDataMimic, + SizedDataMimic, SizedDataMimic, SizedDataMimic, SizedDataMimic, @@ -78,7 +78,7 @@ using CachelinePaddedTypes = ::testing::Types< SizedDataMimic, SizedDataMimic, // Mimic small arrays of basic types: - SizedDataMimic, + SizedDataMimic, SizedDataMimic, SizedDataMimic, SizedDataMimic, @@ -89,7 +89,7 @@ using CachelinePaddedTypes = ::testing::Types< SizedDataMimic, SizedDataMimic, // Mimic large arrays of basic types: - SizedDataMimic, + SizedDataMimic, SizedDataMimic, SizedDataMimic, SizedDataMimic, diff --git a/folly/test/ThreadCachedArenaTest.cpp b/folly/test/ThreadCachedArenaTest.cpp index 458239db..8471e939 100644 --- a/folly/test/ThreadCachedArenaTest.cpp +++ b/folly/test/ThreadCachedArenaTest.cpp @@ -92,7 +92,7 @@ void ArenaTester::merge(ArenaTester&& other) { } // namespace TEST(ThreadCachedArena, BlockSize) { - static const size_t alignment = alignof(std::max_align_t); + static const size_t alignment = folly::max_align_v; static const size_t requestedBlockSize = 64; ThreadCachedArena arena(requestedBlockSize); -- 2.34.1