diff options
| author | Eric Fiselier <eric@efcs.ca> | 2018-10-25 17:21:30 +0000 |
|---|---|---|
| committer | Eric Fiselier <eric@efcs.ca> | 2018-10-25 17:21:30 +0000 |
| commit | f25b75b91b2d5906609bb609d39cf0ee3dcc746b (patch) | |
| tree | 2e9ef3bba902796aa4763c731b857b545f2c4343 /libcxx/test | |
| parent | 1e6d0aad7e90744dcaa5d9be8644dafd5181834d (diff) | |
| download | bcm5719-llvm-f25b75b91b2d5906609bb609d39cf0ee3dcc746b.tar.gz bcm5719-llvm-f25b75b91b2d5906609bb609d39cf0ee3dcc746b.zip | |
Implement sized deallocation for std::allocator and friends.
Summary:
C++14 sized deallocation is disabled by default due to ABI concerns. However, when a user manually enables it then libc++ should take advantage of it since sized deallocation can provide a significant performance win depending on the underlying malloc implementation. (Note that libc++'s definitions of sized delete don't do anything special yet, but users are free to provide their own).
This patch updates __libcpp_deallocate to selectively call sized operator delete when it's available. `__libcpp_deallocate_unsized` should be used when the size of the allocation is unknown.
On Apple this patch makes no attempt to determine if the sized operator delete is unavailable, only that the language feature is enabled. This could cause a compile error when using `std::allocator`, but the same compile error would occur whenever the user calls `new`, so I don't think it's a problem.
Reviewers: ldionne, mclow.lists
Reviewed By: ldionne
Subscribers: rsmith, ckennelly, libcxx-commits, christof
Differential Revision: https://reviews.llvm.org/D53120
llvm-svn: 345281
Diffstat (limited to 'libcxx/test')
| -rw-r--r-- | libcxx/test/libcxx/language.support/support.dynamic/libcpp_deallocate.sh.cpp | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/libcpp_deallocate.sh.cpp b/libcxx/test/libcxx/language.support/support.dynamic/libcpp_deallocate.sh.cpp new file mode 100644 index 00000000000..3631ffe204e --- /dev/null +++ b/libcxx/test/libcxx/language.support/support.dynamic/libcpp_deallocate.sh.cpp @@ -0,0 +1,246 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// test libc++'s implementation of align_val_t, and the relevent new/delete +// overloads in all dialects when -faligned-allocation is present. + +// Libc++ defers to the underlying MSVC library to provide the new/delete +// definitions, which does not yet provide aligned allocation +// XFAIL: LIBCXX-WINDOWS-FIXME + +// XFAIL: with_system_cxx_lib=macosx10.12 +// XFAIL: with_system_cxx_lib=macosx10.11 +// XFAIL: with_system_cxx_lib=macosx10.10 +// XFAIL: with_system_cxx_lib=macosx10.9 +// XFAIL: with_system_cxx_lib=macosx10.7 +// XFAIL: with_system_cxx_lib=macosx10.8 + +// XFAIL: sanitizer-new-delete, ubsan + +// RUN: %build -faligned-allocation -fsized-deallocation +// RUN: %run +// RUN: %build -faligned-allocation -fno-sized-deallocation -DNO_SIZE +// RUN: %run +// RUN: %build -fno-aligned-allocation -fsized-deallocation -DNO_ALIGN +// RUN: %run +// RUN: %build -fno-aligned-allocation -fno-sized-deallocation -DNO_ALIGN -DNO_SIZE +// RUN: %run + +#include <new> +#include <typeinfo> +#include <string> +#include <cassert> + +#include "test_macros.h" + +struct alloc_stats { + alloc_stats() { reset(); } + + int aligned_sized_called; + int aligned_called; + int sized_called; + int plain_called; + int last_size; + int last_align; + + void reset() { + aligned_sized_called = aligned_called = sized_called = plain_called = 0; + last_align = last_size = -1; + } + + bool expect_plain() const { + assert(aligned_sized_called == 0); + assert(aligned_called == 0); + assert(sized_called == 0); + assert(last_size == -1); + assert(last_align == -1); + return plain_called == 1; + } + + bool expect_size(int n) const { + assert(plain_called == 0); + assert(aligned_sized_called == 0); + assert(aligned_called == 0); + assert(last_size == n); + assert(last_align == -1); + return sized_called == 1; + } + + bool expect_align(int a) const { + assert(plain_called == 0); + assert(aligned_sized_called == 0); + assert(sized_called == 0); + assert(last_size == -1); + assert(last_align == a); + return aligned_called == 1; + } + + bool expect_size_align(int n, int a) const { + assert(plain_called == 0); + assert(sized_called == 0); + assert(aligned_called == 0); + assert(last_size == n); + assert(last_align == a); + return aligned_sized_called == 1; + } +}; +alloc_stats stats; + +void operator delete(void* p)TEST_NOEXCEPT { + ::free(p); + stats.plain_called++; + stats.last_size = stats.last_align = -1; +} + +#ifndef NO_SIZE +void operator delete(void* p, size_t n)TEST_NOEXCEPT { + ::free(p); + stats.sized_called++; + stats.last_size = n; + stats.last_align = -1; +} +#endif + +#ifndef NO_ALIGN +void operator delete(void* p, std::align_val_t a)TEST_NOEXCEPT { + ::free(p); + stats.aligned_called++; + stats.last_align = static_cast<int>(a); + stats.last_size = -1; +} + +void operator delete(void* p, size_t n, std::align_val_t a)TEST_NOEXCEPT { + ::free(p); + stats.aligned_sized_called++; + stats.last_align = static_cast<int>(a); + stats.last_size = n; +} +#endif + +void test_libcpp_dealloc() { + void* p = nullptr; + size_t over_align_val = TEST_ALIGNOF(std::max_align_t) * 2; + size_t under_align_val = TEST_ALIGNOF(int); + size_t with_size_val = 2; + + { + std::__libcpp_deallocate_unsized(p, under_align_val); + assert(stats.expect_plain()); + } + stats.reset(); + +#if defined(NO_SIZE) && defined(NO_ALIGN) + { + std::__libcpp_deallocate(p, with_size_val, over_align_val); + assert(stats.expect_plain()); + } + stats.reset(); +#elif defined(NO_SIZE) + { + std::__libcpp_deallocate(p, with_size_val, over_align_val); + assert(stats.expect_align(over_align_val)); + } + stats.reset(); +#elif defined(NO_ALIGN) + { + std::__libcpp_deallocate(p, with_size_val, over_align_val); + assert(stats.expect_size(with_size_val)); + } + stats.reset(); +#else + { + std::__libcpp_deallocate(p, with_size_val, over_align_val); + assert(stats.expect_size_align(with_size_val, over_align_val)); + } + stats.reset(); + { + std::__libcpp_deallocate_unsized(p, over_align_val); + assert(stats.expect_align(over_align_val)); + } + stats.reset(); + { + std::__libcpp_deallocate(p, with_size_val, under_align_val); + assert(stats.expect_size(with_size_val)); + } + stats.reset(); +#endif +} + +struct TEST_ALIGNAS(128) AlignedType { + AlignedType() : elem(0) {} + TEST_ALIGNAS(128) char elem; +}; + +void test_allocator_and_new_match() { + stats.reset(); +#if defined(NO_SIZE) && defined(NO_ALIGN) + { + int* x = new int(42); + delete x; + assert(stats.expect_plain()); + } + stats.reset(); + { + AlignedType* a = new AlignedType(); + delete a; + assert(stats.expect_plain()); + } + stats.reset(); +#elif defined(NO_SIZE) + stats.reset(); +#if TEST_STD_VER >= 11 + { + int* x = new int(42); + delete x; + assert(stats.expect_plain()); + } +#endif + stats.reset(); + { + AlignedType* a = new AlignedType(); + delete a; + assert(stats.expect_align(TEST_ALIGNOF(AlignedType))); + } + stats.reset(); +#elif defined(NO_ALIGN) + stats.reset(); + { + int* x = new int(42); + delete x; + assert(stats.expect_size(sizeof(int))); + } + stats.reset(); + { + AlignedType* a = new AlignedType(); + delete a; + assert(stats.expect_size(sizeof(AlignedType))); + } + stats.reset(); +#else + stats.reset(); + { + int* x = new int(42); + delete x; + assert(stats.expect_size(sizeof(int))); + } + stats.reset(); + { + AlignedType* a = new AlignedType(); + delete a; + assert(stats.expect_size_align(sizeof(AlignedType), + TEST_ALIGNOF(AlignedType))); + } + stats.reset(); +#endif +} + +int main() { + test_libcpp_dealloc(); + test_allocator_and_new_match(); +} |

