PR libstdc++/70940 make pmr::resource_adaptor return aligned memory

PR libstdc++/70940
	* include/experimental/memory_resource (__resource_adaptor_common):
	New base class.
	(__resource_adaptor_common::_AlignMgr): Helper for obtaining aligned
	pointer from unaligned, and vice versa.
	(__resource_adaptor_imp::do_allocate): Use _AlignMgr to adjust
	allocated pointer to meet alignment request.
	(__resource_adaptor_imp::do_deallocate): Use _AlignMgr to retrieve
	original pointer for deallocation.
	(__resource_adaptor_imp::do_is_equal): Reformat.
	(__resource_adaptor_imp::_S_aligned_size): Remove.
	(__resource_adaptor_imp::_S_supported): Remove.
	(new_delete_resource): Use __gnu_cxx::new_allocator.
	* testsuite/experimental/memory_resource/resource_adaptor.cc: Test
	extended alignments and use debug_allocator to check for matching
	allocate/deallocate pairs.

From-SVN: r261849
This commit is contained in:
Jonathan Wakely 2018-06-21 15:01:11 +01:00 committed by Jonathan Wakely
parent 67b3b8feb3
commit 7956c508dd
3 changed files with 186 additions and 39 deletions

View File

@ -1,3 +1,22 @@
2018-06-21 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/70940
* include/experimental/memory_resource (__resource_adaptor_common):
New base class.
(__resource_adaptor_common::_AlignMgr): Helper for obtaining aligned
pointer from unaligned, and vice versa.
(__resource_adaptor_imp::do_allocate): Use _AlignMgr to adjust
allocated pointer to meet alignment request.
(__resource_adaptor_imp::do_deallocate): Use _AlignMgr to retrieve
original pointer for deallocation.
(__resource_adaptor_imp::do_is_equal): Reformat.
(__resource_adaptor_imp::_S_aligned_size): Remove.
(__resource_adaptor_imp::_S_supported): Remove.
(new_delete_resource): Use __gnu_cxx::new_allocator.
* testsuite/experimental/memory_resource/resource_adaptor.cc: Test
extended alignments and use debug_allocator to check for matching
allocate/deallocate pairs.
2018-06-21 François Dumont <fdumont@gcc.gnu.org>
* include/debug/debug.h

View File

@ -33,6 +33,7 @@
#include <new>
#include <atomic>
#include <cstddef>
#include <ext/new_allocator.h>
#include <experimental/bits/lfts_config.h>
namespace std {
@ -253,9 +254,103 @@ namespace pmr {
const polymorphic_allocator<_Tp2>& __b) noexcept
{ return !(__a == __b); }
class __resource_adaptor_common
{
template<typename> friend class __resource_adaptor_imp;
struct _AlignMgr
{
_AlignMgr(size_t __nbytes, size_t __align)
: _M_nbytes(__nbytes), _M_align(__align)
{ }
// Total size that needs to be allocated.
size_t
_M_alloc_size() const { return _M_buf_size() + _M_token_size(); }
void*
_M_adjust(void* __ptr) const
{
const auto __orig_ptr = static_cast<char*>(__ptr);
size_t __space = _M_buf_size();
// Align the pointer within the buffer:
std::align(_M_align, _M_nbytes, __ptr, __space);
const auto __aligned_ptr = static_cast<char*>(__ptr);
const auto __token_size = _M_token_size();
// Store token immediately after the aligned block:
char* const __end = __aligned_ptr + _M_nbytes;
if (__token_size == 1)
_S_write<unsigned char>(__end, __aligned_ptr - __orig_ptr);
else if (__token_size == sizeof(short))
_S_write<unsigned short>(__end, __aligned_ptr - __orig_ptr);
else if (__token_size == sizeof(int) && sizeof(int) < sizeof(char*))
_S_write<unsigned int>(__end, __aligned_ptr - __orig_ptr);
else // (__token_size == sizeof(char*))
// Just store the original pointer:
_S_write<char*>(__end, __orig_ptr);
return __aligned_ptr;
}
char*
_M_unadjust(char* __ptr) const
{
const char* const __end = __ptr + _M_nbytes;
char* __orig_ptr;
const auto __token_size = _M_token_size();
// Read the token and restore the original pointer:
if (__token_size == 1)
__orig_ptr = __ptr - _S_read<unsigned char>(__end);
else if (__token_size == sizeof(short))
__orig_ptr = __ptr - _S_read<unsigned short>(__end);
else if (__token_size == sizeof(int)
&& sizeof(int) < sizeof(char*))
__orig_ptr = __ptr - _S_read<unsigned int>(__end);
else // (__token_size == sizeof(char*))
__orig_ptr = _S_read<char*>(__end);
return __orig_ptr;
}
private:
size_t _M_nbytes;
size_t _M_align;
// Number of bytes needed to fit block of given size and alignment.
size_t
_M_buf_size() const { return _M_nbytes + _M_align - 1; }
// Number of additional bytes needed to write the token.
int
_M_token_size() const
{
if (_M_align <= (1ul << __CHAR_BIT__))
return 1;
if (_M_align <= (1ul << (sizeof(short) * __CHAR_BIT__)))
return sizeof(short);
if (_M_align <= (1ul << (sizeof(int) * __CHAR_BIT__)))
return sizeof(int);
return sizeof(char*);
}
template<typename _Tp>
static void
_S_write(void* __to, _Tp __val)
{ __builtin_memcpy(__to, &__val, sizeof(_Tp)); }
template<typename _Tp>
static _Tp
_S_read(const void* __from)
{
_Tp __val;
__builtin_memcpy(&__val, __from, sizeof(_Tp));
return __val;
}
};
};
// 8.7.1 __resource_adaptor_imp
template <typename _Alloc>
class __resource_adaptor_imp : public memory_resource
class __resource_adaptor_imp
: public memory_resource, private __resource_adaptor_common
{
static_assert(is_same<char,
typename allocator_traits<_Alloc>::value_type>::value,
@ -295,50 +390,41 @@ namespace pmr {
protected:
virtual void*
do_allocate(size_t __bytes, size_t __alignment)
do_allocate(size_t __bytes, size_t __alignment) override
{
using _Aligned_alloc = std::__alloc_rebind<_Alloc, char>;
size_t __new_size = _S_aligned_size(__bytes,
_S_supported(__alignment) ?
__alignment : _S_max_align);
return _Aligned_alloc(_M_alloc).allocate(__new_size);
if (__alignment == 1)
return _M_alloc.allocate(__bytes);
const _AlignMgr __mgr(__bytes, __alignment);
// Assume _M_alloc returns 1-byte aligned memory, so allocate enough
// space to fit a block of the right size and alignment, plus some
// extra bytes to store a token for retrieving the original pointer.
return __mgr._M_adjust(_M_alloc.allocate(__mgr._M_alloc_size()));
}
virtual void
do_deallocate(void* __p, size_t __bytes, size_t __alignment)
do_deallocate(void* __p, size_t __bytes, size_t __alignment) noexcept
override
{
using _Aligned_alloc = std::__alloc_rebind<_Alloc, char>;
size_t __new_size = _S_aligned_size(__bytes,
_S_supported(__alignment) ?
__alignment : _S_max_align);
using _Ptr = typename allocator_traits<_Aligned_alloc>::pointer;
_Aligned_alloc(_M_alloc).deallocate(static_cast<_Ptr>(__p),
__new_size);
auto __ptr = static_cast<char*>(__p);
if (__alignment == 1)
_M_alloc.deallocate(__ptr, __bytes);
const _AlignMgr __mgr(__bytes, __alignment);
// Use the stored token to retrieve the original pointer to deallocate.
_M_alloc.deallocate(__mgr._M_unadjust(__ptr), __mgr._M_alloc_size());
}
virtual bool
do_is_equal(const memory_resource& __other) const noexcept
do_is_equal(const memory_resource& __other) const noexcept override
{
auto __p = dynamic_cast<const __resource_adaptor_imp*>(&__other);
return __p ? (_M_alloc == __p->_M_alloc) : false;
if (auto __p = dynamic_cast<const __resource_adaptor_imp*>(&__other))
return _M_alloc == __p->_M_alloc;
return false;
}
private:
// Calculate Aligned Size
// Returns a size that is larger than or equal to __size and divisible
// by __alignment, where __alignment is required to be a power of 2.
static size_t
_S_aligned_size(size_t __size, size_t __alignment)
{ return ((__size - 1)|(__alignment - 1)) + 1; }
// Determine whether alignment meets one of those preconditions:
// 1. Equal to Zero
// 2. Is power of two
static bool
_S_supported (size_t __x)
{ return ((__x != 0) && !(__x & (__x - 1))); }
_Alloc _M_alloc;
_Alloc _M_alloc{};
};
// Global memory resources
@ -352,7 +438,7 @@ namespace pmr {
inline memory_resource*
new_delete_resource() noexcept
{
using type = resource_adaptor<std::allocator<char>>;
using type = resource_adaptor<__gnu_cxx::new_allocator<char>>;
alignas(type) static unsigned char __buf[sizeof(type)];
static type* __r = new(__buf) type;
return __r;

View File

@ -19,6 +19,7 @@
// <http://www.gnu.org/licenses/>.
#include <experimental/memory_resource>
#include <ext/debug_allocator.h>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>
@ -34,18 +35,22 @@ template<typename T>
Allocator(const Allocator<U>&) { }
};
template<typename T>
template<std::size_t A>
bool aligned(void* p)
{
return (reinterpret_cast<std::uintptr_t>(p) % alignof(T)) == 0;
return (reinterpret_cast<std::uintptr_t>(p) % A) == 0;
}
template<typename T>
bool aligned(void* p)
{ return aligned<alignof(T)>(p); }
// resource_adaptor
void
test05()
{
using std::max_align_t;
using std::uintptr_t;
using std::size_t;
void* p = nullptr;
Allocator<int> a1(1), a2(2); // minimal interface allocators
@ -61,12 +66,18 @@ test05()
p = r1.allocate(1, alignof(long));
VERIFY( aligned<long>(p) );
r1.deallocate(p, 1, alignof(long));
constexpr size_t big_al = alignof(max_align_t) * 8;
p = r1.allocate(1, big_al);
VERIFY( aligned<big_al>(p) );
r1.deallocate(p, 1, big_al);
__gnu_test::uneq_allocator<double> a3(3), a4(4); // non-equal allocators
resource_adaptor<decltype(a3)> r3(a3), r4(a4);
VERIFY( r3 == r3 );
VERIFY( r4 == r4 );
VERIFY( r3 != r4 );
VERIFY( r3 != r1 );
VERIFY( r3 != r2 );
p = r3.allocate(1);
VERIFY( aligned<max_align_t>(p) );
r3.deallocate(p, 1);
@ -76,9 +87,40 @@ test05()
p = r3.allocate(1, alignof(long));
VERIFY( aligned<long>(p) );
r3.deallocate(p, 1, alignof(long));
p = r3.allocate(1, big_al);
VERIFY( aligned<big_al>(p) );
r3.deallocate(p, 1, big_al);
// TODO test with an allocator that doesn't use new or malloc, so
// returns pointers that are not suitably aligned for any type.
__gnu_cxx::debug_allocator<std::allocator<short>> a5;
resource_adaptor<decltype(a5)> r5(a5), r6(a5);
VERIFY( r5 == r5 );
VERIFY( r5 == r6 );
VERIFY( r5 != r1 );
VERIFY( r5 != r3 );
p = r5.allocate(1);
VERIFY( aligned<max_align_t>(p) );
r5.deallocate(p, 1);
p = r5.allocate(1, alignof(short));
VERIFY( aligned<short>(p) );
r5.deallocate(p, 1, alignof(short));
p = r5.allocate(1, alignof(long));
VERIFY( aligned<long>(p) );
r5.deallocate(p, 1, alignof(long));
p = r5.allocate(1, big_al);
VERIFY( aligned<big_al>(p) );
r5.deallocate(p, 1, big_al);
// Test extended alignments
constexpr size_t al6 = (1ul << 6), al12 = (1ul << 12), al18 = (1ul << 18);
p = r5.allocate(1024, al6);
VERIFY( aligned<al6>(p) );
r5.deallocate(p, 1024, al6);
p = r5.allocate(1024, al12);
VERIFY( aligned<al12>(p) );
r5.deallocate(p, 1024, al12);
p = r5.allocate(1024, al18);
VERIFY( aligned<al18>(p) );
r5.deallocate(p, 1024, al18);
}
int main()