Fix PR22634 - std::allocator doesn't respect over-aligned types.
This patch fixes std::allocator, and more specifically, all users of __libcpp_allocate and __libcpp_deallocate, to support over-aligned types. __libcpp_allocate/deallocate now take an alignment parameter, and when the specified alignment is greater than that supported by malloc/new, the aligned version of operator new is called (assuming it's available). When aligned new isn't available, the old behavior has been kept, and the alignment parameter is ignored. This patch depends on recent changes to __builtin_operator_new/delete which allow them to be used to call any regular new/delete operator. By using __builtin_operator_new/delete when possible, the new/delete erasure optimization is maintained. git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@328180 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -15,39 +15,93 @@
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "count_new.hpp"
|
||||
|
||||
int A_constructed = 0;
|
||||
|
||||
struct A
|
||||
{
|
||||
int data;
|
||||
A() {++A_constructed;}
|
||||
A(const A&) {++A_constructed;}
|
||||
~A() {--A_constructed;}
|
||||
#ifdef TEST_HAS_NO_ALIGNED_ALLOCATION
|
||||
static const bool UsingAlignedNew = false;
|
||||
#else
|
||||
static const bool UsingAlignedNew = true;
|
||||
#endif
|
||||
|
||||
#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
|
||||
static const size_t MaxAligned = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
|
||||
#else
|
||||
static const size_t MaxAligned = std::alignment_of<std::max_align_t>::value;
|
||||
#endif
|
||||
|
||||
static const size_t OverAligned = MaxAligned * 2;
|
||||
|
||||
|
||||
template <size_t Align>
|
||||
struct TEST_ALIGNAS(Align) AlignedType {
|
||||
char data;
|
||||
static int constructed;
|
||||
AlignedType() { ++constructed; }
|
||||
AlignedType(AlignedType const&) { ++constructed; }
|
||||
~AlignedType() { --constructed; }
|
||||
};
|
||||
template <size_t Align>
|
||||
int AlignedType<Align>::constructed = 0;
|
||||
|
||||
int main()
|
||||
{
|
||||
globalMemCounter.reset();
|
||||
std::allocator<A> a;
|
||||
|
||||
template <size_t Align>
|
||||
void test_aligned() {
|
||||
typedef AlignedType<Align> T;
|
||||
T::constructed = 0;
|
||||
globalMemCounter.reset();
|
||||
std::allocator<T> a;
|
||||
const bool IsOverAlignedType = Align > MaxAligned;
|
||||
const bool ExpectAligned = IsOverAlignedType && UsingAlignedNew;
|
||||
{
|
||||
assert(globalMemCounter.checkOutstandingNewEq(0));
|
||||
assert(A_constructed == 0);
|
||||
assert(T::constructed == 0);
|
||||
globalMemCounter.last_new_size = 0;
|
||||
A* volatile ap = a.allocate(3);
|
||||
globalMemCounter.last_new_align = 0;
|
||||
T* volatile ap = a.allocate(3);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkLastNewSizeEq(3 * sizeof(int)));
|
||||
assert(A_constructed == 0);
|
||||
assert(globalMemCounter.checkNewCalledEq(1));
|
||||
assert(globalMemCounter.checkAlignedNewCalledEq(ExpectAligned));
|
||||
assert(globalMemCounter.checkLastNewSizeEq(3 * sizeof(T)));
|
||||
assert(globalMemCounter.checkLastNewAlignEq(ExpectAligned ? Align : 0));
|
||||
assert(T::constructed == 0);
|
||||
globalMemCounter.last_delete_align = 0;
|
||||
a.deallocate(ap, 3);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(0));
|
||||
assert(A_constructed == 0);
|
||||
|
||||
assert(globalMemCounter.checkDeleteCalledEq(1));
|
||||
assert(globalMemCounter.checkAlignedDeleteCalledEq(ExpectAligned));
|
||||
assert(globalMemCounter.checkLastDeleteAlignEq(ExpectAligned ? Align : 0));
|
||||
assert(T::constructed == 0);
|
||||
}
|
||||
globalMemCounter.reset();
|
||||
{
|
||||
globalMemCounter.last_new_size = 0;
|
||||
A* volatile ap2 = a.allocate(3, (const void*)5);
|
||||
globalMemCounter.last_new_align = 0;
|
||||
T* volatile ap2 = a.allocate(11, (const void*)5);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(1));
|
||||
assert(globalMemCounter.checkLastNewSizeEq(3 * sizeof(int)));
|
||||
assert(A_constructed == 0);
|
||||
a.deallocate(ap2, 3);
|
||||
assert(globalMemCounter.checkNewCalledEq(1));
|
||||
assert(globalMemCounter.checkAlignedNewCalledEq(ExpectAligned));
|
||||
assert(globalMemCounter.checkLastNewSizeEq(11 * sizeof(T)));
|
||||
assert(globalMemCounter.checkLastNewAlignEq(ExpectAligned ? Align : 0));
|
||||
assert(T::constructed == 0);
|
||||
globalMemCounter.last_delete_align = 0;
|
||||
a.deallocate(ap2, 11);
|
||||
assert(globalMemCounter.checkOutstandingNewEq(0));
|
||||
assert(A_constructed == 0);
|
||||
assert(globalMemCounter.checkDeleteCalledEq(1));
|
||||
assert(globalMemCounter.checkAlignedDeleteCalledEq(ExpectAligned));
|
||||
assert(globalMemCounter.checkLastDeleteAlignEq(ExpectAligned ? Align : 0));
|
||||
assert(T::constructed == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_aligned<1>();
|
||||
test_aligned<2>();
|
||||
test_aligned<4>();
|
||||
test_aligned<8>();
|
||||
test_aligned<16>();
|
||||
test_aligned<MaxAligned>();
|
||||
test_aligned<OverAligned>();
|
||||
test_aligned<OverAligned * 2>();
|
||||
}
|
||||
|
||||
@@ -59,12 +59,20 @@ public:
|
||||
int outstanding_new;
|
||||
int new_called;
|
||||
int delete_called;
|
||||
int aligned_new_called;
|
||||
int aligned_delete_called;
|
||||
std::size_t last_new_size;
|
||||
std::size_t last_new_align;
|
||||
std::size_t last_delete_align;
|
||||
|
||||
int outstanding_array_new;
|
||||
int new_array_called;
|
||||
int delete_array_called;
|
||||
int aligned_new_array_called;
|
||||
int aligned_delete_array_called;
|
||||
std::size_t last_new_array_size;
|
||||
std::size_t last_new_array_align;
|
||||
std::size_t last_delete_array_align;
|
||||
|
||||
public:
|
||||
void newCalled(std::size_t s)
|
||||
@@ -82,6 +90,12 @@ public:
|
||||
last_new_size = s;
|
||||
}
|
||||
|
||||
void alignedNewCalled(std::size_t s, std::size_t a) {
|
||||
newCalled(s);
|
||||
++aligned_new_called;
|
||||
last_new_align = a;
|
||||
}
|
||||
|
||||
void deleteCalled(void * p)
|
||||
{
|
||||
assert(p);
|
||||
@@ -89,6 +103,12 @@ public:
|
||||
++delete_called;
|
||||
}
|
||||
|
||||
void alignedDeleteCalled(void *p, std::size_t a) {
|
||||
deleteCalled(p);
|
||||
++aligned_delete_called;
|
||||
last_delete_align = a;
|
||||
}
|
||||
|
||||
void newArrayCalled(std::size_t s)
|
||||
{
|
||||
assert(disable_allocations == false);
|
||||
@@ -104,6 +124,12 @@ public:
|
||||
last_new_array_size = s;
|
||||
}
|
||||
|
||||
void alignedNewArrayCalled(std::size_t s, std::size_t a) {
|
||||
newArrayCalled(s);
|
||||
++aligned_new_array_called;
|
||||
last_new_array_align = a;
|
||||
}
|
||||
|
||||
void deleteArrayCalled(void * p)
|
||||
{
|
||||
assert(p);
|
||||
@@ -111,6 +137,12 @@ public:
|
||||
++delete_array_called;
|
||||
}
|
||||
|
||||
void alignedDeleteArrayCalled(void * p, std::size_t a) {
|
||||
deleteArrayCalled(p);
|
||||
++aligned_delete_array_called;
|
||||
last_delete_array_align = a;
|
||||
}
|
||||
|
||||
void disableAllocations()
|
||||
{
|
||||
disable_allocations = true;
|
||||
@@ -121,7 +153,6 @@ public:
|
||||
disable_allocations = false;
|
||||
}
|
||||
|
||||
|
||||
void reset()
|
||||
{
|
||||
disable_allocations = false;
|
||||
@@ -130,12 +161,18 @@ public:
|
||||
outstanding_new = 0;
|
||||
new_called = 0;
|
||||
delete_called = 0;
|
||||
aligned_new_called = 0;
|
||||
aligned_delete_called = 0;
|
||||
last_new_size = 0;
|
||||
last_new_align = 0;
|
||||
|
||||
outstanding_array_new = 0;
|
||||
new_array_called = 0;
|
||||
delete_array_called = 0;
|
||||
aligned_new_array_called = 0;
|
||||
aligned_delete_array_called = 0;
|
||||
last_new_array_size = 0;
|
||||
last_new_array_align = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -174,6 +211,31 @@ public:
|
||||
return disable_checking || n != delete_called;
|
||||
}
|
||||
|
||||
bool checkAlignedNewCalledEq(int n) const
|
||||
{
|
||||
return disable_checking || n == aligned_new_called;
|
||||
}
|
||||
|
||||
bool checkAlignedNewCalledNotEq(int n) const
|
||||
{
|
||||
return disable_checking || n != aligned_new_called;
|
||||
}
|
||||
|
||||
bool checkAlignedNewCalledGreaterThan(int n) const
|
||||
{
|
||||
return disable_checking || aligned_new_called > n;
|
||||
}
|
||||
|
||||
bool checkAlignedDeleteCalledEq(int n) const
|
||||
{
|
||||
return disable_checking || n == aligned_delete_called;
|
||||
}
|
||||
|
||||
bool checkAlignedDeleteCalledNotEq(int n) const
|
||||
{
|
||||
return disable_checking || n != aligned_delete_called;
|
||||
}
|
||||
|
||||
bool checkLastNewSizeEq(std::size_t n) const
|
||||
{
|
||||
return disable_checking || n == last_new_size;
|
||||
@@ -184,6 +246,26 @@ public:
|
||||
return disable_checking || n != last_new_size;
|
||||
}
|
||||
|
||||
bool checkLastNewAlignEq(std::size_t n) const
|
||||
{
|
||||
return disable_checking || n == last_new_align;
|
||||
}
|
||||
|
||||
bool checkLastNewAlignNotEq(std::size_t n) const
|
||||
{
|
||||
return disable_checking || n != last_new_align;
|
||||
}
|
||||
|
||||
bool checkLastDeleteAlignEq(std::size_t n) const
|
||||
{
|
||||
return disable_checking || n == last_delete_align;
|
||||
}
|
||||
|
||||
bool checkLastDeleteAlignNotEq(std::size_t n) const
|
||||
{
|
||||
return disable_checking || n != last_delete_align;
|
||||
}
|
||||
|
||||
bool checkOutstandingArrayNewEq(int n) const
|
||||
{
|
||||
return disable_checking || n == outstanding_array_new;
|
||||
@@ -214,6 +296,31 @@ public:
|
||||
return disable_checking || n != delete_array_called;
|
||||
}
|
||||
|
||||
bool checkAlignedNewArrayCalledEq(int n) const
|
||||
{
|
||||
return disable_checking || n == aligned_new_array_called;
|
||||
}
|
||||
|
||||
bool checkAlignedNewArrayCalledNotEq(int n) const
|
||||
{
|
||||
return disable_checking || n != aligned_new_array_called;
|
||||
}
|
||||
|
||||
bool checkAlignedNewArrayCalledGreaterThan(int n) const
|
||||
{
|
||||
return disable_checking || aligned_new_array_called > n;
|
||||
}
|
||||
|
||||
bool checkAlignedDeleteArrayCalledEq(int n) const
|
||||
{
|
||||
return disable_checking || n == aligned_delete_array_called;
|
||||
}
|
||||
|
||||
bool checkAlignedDeleteArrayCalledNotEq(int n) const
|
||||
{
|
||||
return disable_checking || n != aligned_delete_array_called;
|
||||
}
|
||||
|
||||
bool checkLastNewArraySizeEq(std::size_t n) const
|
||||
{
|
||||
return disable_checking || n == last_new_array_size;
|
||||
@@ -223,6 +330,16 @@ public:
|
||||
{
|
||||
return disable_checking || n != last_new_array_size;
|
||||
}
|
||||
|
||||
bool checkLastNewArrayAlignEq(std::size_t n) const
|
||||
{
|
||||
return disable_checking || n == last_new_array_align;
|
||||
}
|
||||
|
||||
bool checkLastNewArrayAlignNotEq(std::size_t n) const
|
||||
{
|
||||
return disable_checking || n != last_new_array_align;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef DISABLE_NEW_COUNT
|
||||
@@ -254,22 +371,65 @@ void operator delete(void* p) TEST_NOEXCEPT
|
||||
std::free(p);
|
||||
}
|
||||
|
||||
|
||||
void* operator new[](std::size_t s) TEST_THROW_SPEC(std::bad_alloc)
|
||||
{
|
||||
getGlobalMemCounter()->newArrayCalled(s);
|
||||
return operator new(s);
|
||||
}
|
||||
|
||||
|
||||
void operator delete[](void* p) TEST_NOEXCEPT
|
||||
{
|
||||
getGlobalMemCounter()->deleteArrayCalled(p);
|
||||
operator delete(p);
|
||||
}
|
||||
|
||||
#endif // DISABLE_NEW_COUNT
|
||||
#ifndef TEST_HAS_NO_ALIGNED_ALLOCATION
|
||||
#if defined(_LIBCPP_MSVCRT_LIKE) || \
|
||||
(!defined(_LIBCPP_VERSION) && defined(_WIN32))
|
||||
#define USE_ALIGNED_ALLOC
|
||||
#endif
|
||||
|
||||
void* operator new(std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
|
||||
const std::size_t a = static_cast<std::size_t>(av);
|
||||
getGlobalMemCounter()->alignedNewCalled(s, a);
|
||||
void *ret;
|
||||
#ifdef USE_ALIGNED_ALLOC
|
||||
ret = _aligned_malloc(s, a);
|
||||
#else
|
||||
posix_memalign(&ret, a, s);
|
||||
#endif
|
||||
if (ret == nullptr)
|
||||
detail::throw_bad_alloc_helper();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void operator delete(void *p, std::align_val_t av) TEST_NOEXCEPT {
|
||||
const std::size_t a = static_cast<std::size_t>(av);
|
||||
getGlobalMemCounter()->alignedDeleteCalled(p, a);
|
||||
if (p) {
|
||||
#ifdef USE_ALIGNED_ALLOC
|
||||
::_aligned_free(p);
|
||||
#else
|
||||
::free(p);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
|
||||
const std::size_t a = static_cast<std::size_t>(av);
|
||||
getGlobalMemCounter()->alignedNewArrayCalled(s, a);
|
||||
return operator new(s, av);
|
||||
}
|
||||
|
||||
void operator delete[](void *p, std::align_val_t av) TEST_NOEXCEPT {
|
||||
const std::size_t a = static_cast<std::size_t>(av);
|
||||
getGlobalMemCounter()->alignedDeleteArrayCalled(p, a);
|
||||
return operator delete(p, av);
|
||||
}
|
||||
|
||||
#endif // TEST_HAS_NO_ALIGNED_ALLOCATION
|
||||
|
||||
#endif // DISABLE_NEW_COUNT
|
||||
|
||||
struct DisableAllocationGuard {
|
||||
explicit DisableAllocationGuard(bool disable = true) : m_disabled(disable)
|
||||
@@ -295,7 +455,6 @@ private:
|
||||
DisableAllocationGuard& operator=(DisableAllocationGuard const&);
|
||||
};
|
||||
|
||||
|
||||
struct RequireAllocationGuard {
|
||||
explicit RequireAllocationGuard(std::size_t RequireAtLeast = 1)
|
||||
: m_req_alloc(RequireAtLeast),
|
||||
|
||||
@@ -157,6 +157,11 @@
|
||||
#define TEST_NORETURN [[noreturn]]
|
||||
#endif
|
||||
|
||||
#if !defined(__cpp_aligned_new) || __cpp_aligned_new < 201606L || \
|
||||
defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION)
|
||||
#define TEST_HAS_NO_ALIGNED_ALLOCATION
|
||||
#endif
|
||||
|
||||
#if defined(_LIBCPP_SAFE_STATIC)
|
||||
#define TEST_SAFE_STATIC _LIBCPP_SAFE_STATIC
|
||||
#else
|
||||
@@ -228,6 +233,7 @@ inline void DoNotOptimize(Tp const& value) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user