Commit 2177f05a authored by Phil Willoughby's avatar Phil Willoughby Committed by Facebook Github Bot

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
parent 339c14d3
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <cstddef> #include <cstddef>
#include <folly/Portability.h>
#include <folly/concurrency/CacheLocality.h> #include <folly/concurrency/CacheLocality.h>
namespace folly { namespace folly {
...@@ -32,8 +33,8 @@ namespace folly { ...@@ -32,8 +33,8 @@ namespace folly {
template <typename T> template <typename T>
class CachelinePadded { class CachelinePadded {
static_assert( static_assert(
alignof(T) < CacheLocality::kFalseSharingRange, alignof(T) <= folly::max_align_v,
"CachelinePadded does not support types aligned >= a cache-line."); "CachelinePadded does not support over-aligned types.");
public: public:
template <typename... Args> template <typename... Args>
......
...@@ -142,10 +142,11 @@ struct FOLLY_ALIGNED_MAX SpinLockArray { ...@@ -142,10 +142,11 @@ struct FOLLY_ALIGNED_MAX SpinLockArray {
"Invalid size of PaddedSpinLock"); "Invalid size of PaddedSpinLock");
// Check if T can theoretically cross a cache line. // Check if T can theoretically cross a cache line.
static_assert(alignof(std::max_align_t) > 0 && static_assert(
FOLLY_CACHE_LINE_SIZE % alignof(std::max_align_t) == 0 && folly::max_align_v > 0 &&
sizeof(T) <= alignof(std::max_align_t), FOLLY_CACHE_LINE_SIZE % folly::max_align_v == 0 &&
"T can cross cache line boundaries"); sizeof(T) <= folly::max_align_v,
"T can cross cache line boundaries");
char padding_[FOLLY_CACHE_LINE_SIZE]; char padding_[FOLLY_CACHE_LINE_SIZE];
std::array<PaddedSpinLock, N> data_; std::array<PaddedSpinLock, N> data_;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <string.h> #include <string.h>
#include <cstddef> #include <cstddef>
#include <type_traits>
#include <folly/portability/Config.h> #include <folly/portability/Config.h>
...@@ -31,7 +32,75 @@ constexpr bool kHasUnalignedAccess = true; ...@@ -31,7 +32,75 @@ constexpr bool kHasUnalignedAccess = true;
#else #else
constexpr bool kHasUnalignedAccess = false; constexpr bool kHasUnalignedAccess = false;
#endif #endif
}
namespace detail {
template <typename I, I A, I... Bs>
struct integral_max
: std::integral_constant<
I,
(A > integral_max<I, Bs...>::value) ? A
: integral_max<I, Bs...>::value> {
};
template <typename I, size_t A>
struct integral_max<I, A> : std::integral_constant<I, A> {};
template <typename... Ts>
using max_alignment = integral_max<size_t, alignof(Ts)...>;
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 // compiler specific attribute translation
// msvc should come first, so if clang is in msvc mode it gets the right defines // msvc should come first, so if clang is in msvc mode it gets the right defines
...@@ -43,7 +112,7 @@ constexpr bool kHasUnalignedAccess = false; ...@@ -43,7 +112,7 @@ constexpr bool kHasUnalignedAccess = false;
#else #else
# error Cannot define FOLLY_ALIGNED on this platform # error Cannot define FOLLY_ALIGNED on this platform
#endif #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 // NOTE: this will only do checking in msvc with versions that support /analyze
#if _MSC_VER #if _MSC_VER
......
...@@ -257,9 +257,8 @@ void* SimpleAllocator::allocateHard() { ...@@ -257,9 +257,8 @@ void* SimpleAllocator::allocateHard() {
// Install a pointer to ourselves as the allocator. // Install a pointer to ourselves as the allocator.
*reinterpret_cast<SimpleAllocator**>(mem_) = this; *reinterpret_cast<SimpleAllocator**>(mem_) = this;
static_assert( static_assert(
alignof(std::max_align_t) >= sizeof(SimpleAllocator*), folly::max_align_v >= sizeof(SimpleAllocator*), "alignment too small");
"alignment too small"); mem_ += std::min(sz_, folly::max_align_v);
mem_ += std::min(sz_, alignof(std::max_align_t));
// New allocation. // New allocation.
auto mem = mem_; auto mem = mem_;
......
...@@ -390,7 +390,7 @@ class SimpleAllocator { ...@@ -390,7 +390,7 @@ class SimpleAllocator {
if (intptr_t(mem_) % 128 == 0) { if (intptr_t(mem_) % 128 == 0) {
// Avoid allocating pointers that may look like malloc // Avoid allocating pointers that may look like malloc
// pointers. // pointers.
mem_ += std::min(sz_, alignof(std::max_align_t)); mem_ += std::min(sz_, folly::max_align_v);
} }
if (mem_ && (mem_ + sz_ <= end_)) { if (mem_ && (mem_ + sz_ <= end_)) {
auto mem = mem_; auto mem = mem_;
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
/// std::pmr::memory_resource (C++17) as needed for developing a /// std::pmr::memory_resource (C++17) as needed for developing a
/// hazptr prototype. /// hazptr prototype.
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cstddef> #include <folly/Portability.h>
#include <memory> #include <memory>
namespace folly { namespace folly {
...@@ -33,11 +33,11 @@ class memory_resource { ...@@ -33,11 +33,11 @@ class memory_resource {
virtual ~memory_resource() = default; virtual ~memory_resource() = default;
virtual void* allocate( virtual void* allocate(
const size_t bytes, 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( virtual void deallocate(
void* p, void* p,
const size_t bytes, 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(); memory_resource* get_default_resource();
...@@ -70,7 +70,7 @@ inline memory_resource* new_delete_resource() { ...@@ -70,7 +70,7 @@ inline memory_resource* new_delete_resource() {
public: public:
void* allocate( void* allocate(
const size_t bytes, 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)alignment;
void* p = static_cast<void*>(new char[bytes]); void* p = static_cast<void*>(new char[bytes]);
DEBUG_PRINT(this << " " << p << " " << bytes); DEBUG_PRINT(this << " " << p << " " << bytes);
...@@ -79,7 +79,7 @@ inline memory_resource* new_delete_resource() { ...@@ -79,7 +79,7 @@ inline memory_resource* new_delete_resource() {
void deallocate( void deallocate(
void* p, void* p,
const size_t bytes, 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)alignment;
(void)bytes; (void)bytes;
DEBUG_PRINT(p << " " << bytes); DEBUG_PRINT(p << " " << bytes);
......
...@@ -114,7 +114,7 @@ struct IOBuf::HeapFullStorage { ...@@ -114,7 +114,7 @@ struct IOBuf::HeapFullStorage {
HeapStorage hs; HeapStorage hs;
SharedInfo shared; SharedInfo shared;
std::max_align_t align; folly::max_align_t align;
}; };
IOBuf::SharedInfo::SharedInfo() IOBuf::SharedInfo::SharedInfo()
......
...@@ -67,7 +67,7 @@ using CachelinePaddedTypes = ::testing::Types< ...@@ -67,7 +67,7 @@ using CachelinePaddedTypes = ::testing::Types<
SizedData<kCachelineSize / 2>, SizedData<kCachelineSize / 2>,
SizedData<kCachelineSize + kCachelineSize / 2>, SizedData<kCachelineSize + kCachelineSize / 2>,
// Mimic single basic types: // Mimic single basic types:
SizedDataMimic<std::max_align_t>, SizedDataMimic<folly::max_align_t>,
SizedDataMimic<void*>, SizedDataMimic<void*>,
SizedDataMimic<long double>, SizedDataMimic<long double>,
SizedDataMimic<double>, SizedDataMimic<double>,
...@@ -78,7 +78,7 @@ using CachelinePaddedTypes = ::testing::Types< ...@@ -78,7 +78,7 @@ using CachelinePaddedTypes = ::testing::Types<
SizedDataMimic<short>, SizedDataMimic<short>,
SizedDataMimic<char>, SizedDataMimic<char>,
// Mimic small arrays of basic types: // Mimic small arrays of basic types:
SizedDataMimic<std::max_align_t, 3>, SizedDataMimic<folly::max_align_t, 3>,
SizedDataMimic<void*, 3>, SizedDataMimic<void*, 3>,
SizedDataMimic<long double, 3>, SizedDataMimic<long double, 3>,
SizedDataMimic<double, 3>, SizedDataMimic<double, 3>,
...@@ -89,7 +89,7 @@ using CachelinePaddedTypes = ::testing::Types< ...@@ -89,7 +89,7 @@ using CachelinePaddedTypes = ::testing::Types<
SizedDataMimic<short, 3>, SizedDataMimic<short, 3>,
SizedDataMimic<char, 3>, SizedDataMimic<char, 3>,
// Mimic large arrays of basic types: // Mimic large arrays of basic types:
SizedDataMimic<std::max_align_t, kCachelineSize + 3>, SizedDataMimic<folly::max_align_t, kCachelineSize + 3>,
SizedDataMimic<void*, kCachelineSize + 3>, SizedDataMimic<void*, kCachelineSize + 3>,
SizedDataMimic<long double, kCachelineSize + 3>, SizedDataMimic<long double, kCachelineSize + 3>,
SizedDataMimic<double, kCachelineSize + 3>, SizedDataMimic<double, kCachelineSize + 3>,
......
...@@ -92,7 +92,7 @@ void ArenaTester::merge(ArenaTester&& other) { ...@@ -92,7 +92,7 @@ void ArenaTester::merge(ArenaTester&& other) {
} // namespace } // namespace
TEST(ThreadCachedArena, BlockSize) { 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; static const size_t requestedBlockSize = 64;
ThreadCachedArena arena(requestedBlockSize); ThreadCachedArena arena(requestedBlockSize);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment