mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 22:16:05 +00:00

In the test suite, we generally don't use printf or other reporting utilities. It's not that it wouldn't be useful, it's just that some platforms don't support IO. Instead, we try to keep test cases small and self-contained so that we can reasonably easily reproduce failures locally and debug them. This patch removes printf in some of the last places in the test suite that used it. The only remaining places are in a deque test and in the filesystem tests. The filesystem tests are arguably fine to keep using IO, since we're testing <filesystem>. The deque test will be handled separately. Differential Revision: https://reviews.llvm.org/D114282
507 lines
14 KiB
C++
507 lines
14 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SUPPORT_CONTROLLED_ALLOCATORS_H
|
|
#define SUPPORT_CONTROLLED_ALLOCATORS_H
|
|
|
|
#include <memory>
|
|
#include <type_traits>
|
|
#include <cstddef>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cstdint>
|
|
#include <cassert>
|
|
#include "test_macros.h"
|
|
#include "type_id.h"
|
|
|
|
#if TEST_STD_VER < 11
|
|
#error This header requires C++11 or greater
|
|
#endif
|
|
|
|
struct AllocController;
|
|
// 'AllocController' is a concrete type that instruments and controls the
|
|
// behavior of test allocators.
|
|
|
|
template <class T, size_t ID = 0>
|
|
class CountingAllocator;
|
|
// 'CountingAllocator' is an basic implementation of the 'Allocator'
|
|
// requirements that use the 'AllocController' interface.
|
|
|
|
template <class T>
|
|
class MinAlignAllocator;
|
|
// 'MinAlignAllocator' is an instrumented test type which implements the
|
|
// 'Allocator' requirements. 'MinAlignAllocator' ensures that it *never*
|
|
// returns a pointer to over-aligned storage. For example
|
|
// 'MinAlignPointer<char>{}.allocate(...)' will never a 2-byte aligned
|
|
// pointer.
|
|
|
|
template <class T>
|
|
class NullAllocator;
|
|
// 'NullAllocator' is an instrumented test type which implements the
|
|
// 'Allocator' requirements except that 'allocator' and 'deallocate' are
|
|
// nops.
|
|
|
|
|
|
#define DISALLOW_COPY(Type) \
|
|
Type(Type const&) = delete; \
|
|
Type& operator=(Type const&) = delete
|
|
|
|
constexpr std::size_t MaxAlignV = alignof(std::max_align_t);
|
|
|
|
struct TestException {};
|
|
|
|
struct AllocController {
|
|
int copy_constructed = 0;
|
|
int move_constructed = 0;
|
|
|
|
int alive = 0;
|
|
int alloc_count = 0;
|
|
int dealloc_count = 0;
|
|
int is_equal_count = 0;
|
|
|
|
std::size_t alive_size;
|
|
std::size_t allocated_size;
|
|
std::size_t deallocated_size;
|
|
|
|
std::size_t last_size = 0;
|
|
std::size_t last_align = 0;
|
|
void * last_pointer = 0;
|
|
|
|
std::size_t last_alloc_size = 0;
|
|
std::size_t last_alloc_align = 0;
|
|
void * last_alloc_pointer = nullptr;
|
|
|
|
std::size_t last_dealloc_size = 0;
|
|
std::size_t last_dealloc_align = 0;
|
|
void * last_dealloc_pointer = nullptr;
|
|
|
|
bool throw_on_alloc = false;
|
|
|
|
int construct_called = 0;
|
|
void *last_construct_pointer = nullptr;
|
|
TypeID const* last_construct_alloc = nullptr;
|
|
TypeID const* last_construct_type = nullptr;
|
|
TypeID const* last_construct_args = nullptr;
|
|
|
|
int destroy_called = 0;
|
|
void *last_destroy_pointer = nullptr;
|
|
TypeID const* last_destroy_alloc = nullptr;
|
|
TypeID const* last_destroy_type = nullptr;
|
|
|
|
AllocController() = default;
|
|
|
|
void countAlloc(void* p, size_t s, size_t a) {
|
|
++alive;
|
|
++alloc_count;
|
|
alive_size += s;
|
|
allocated_size += s;
|
|
last_pointer = last_alloc_pointer = p;
|
|
last_size = last_alloc_size = s;
|
|
last_align = last_alloc_align = a;
|
|
}
|
|
|
|
void countDealloc(void* p, size_t s, size_t a) {
|
|
--alive;
|
|
++dealloc_count;
|
|
alive_size -= s;
|
|
deallocated_size += s;
|
|
last_pointer = last_dealloc_pointer = p;
|
|
last_size = last_dealloc_size = s;
|
|
last_align = last_dealloc_align = a;
|
|
}
|
|
|
|
template <class ...Args, class Alloc, class Tp>
|
|
void countConstruct(Alloc const&, Tp *p) {
|
|
++construct_called;
|
|
last_construct_pointer = p;
|
|
last_construct_alloc = &makeTypeID<Alloc>();
|
|
last_construct_type = &makeTypeID<Tp>();
|
|
last_construct_args = &makeArgumentID<Args...>();
|
|
}
|
|
|
|
template <class Alloc, class Tp>
|
|
void countDestroy(Alloc const&, Tp *p) {
|
|
++destroy_called;
|
|
last_destroy_alloc = &makeTypeID<Alloc>();
|
|
last_destroy_type = &makeTypeID<Tp>();
|
|
last_destroy_pointer = p;
|
|
}
|
|
|
|
void reset() { std::memset(this, 0, sizeof(*this)); }
|
|
void resetConstructDestroy() {
|
|
construct_called = 0;
|
|
last_construct_pointer = nullptr;
|
|
last_construct_alloc = last_construct_args = last_construct_type = nullptr;
|
|
destroy_called = 0;
|
|
last_destroy_alloc = nullptr;
|
|
last_destroy_pointer = nullptr;
|
|
}
|
|
public:
|
|
bool checkAlloc(void* p, size_t s, size_t a) const {
|
|
return p == last_alloc_pointer &&
|
|
s == last_alloc_size &&
|
|
a == last_alloc_align;
|
|
}
|
|
|
|
bool checkAlloc(void* p, size_t s) const {
|
|
return p == last_alloc_pointer &&
|
|
s == last_alloc_size;
|
|
}
|
|
|
|
bool checkAllocAtLeast(void* p, size_t s, size_t a) const {
|
|
return p == last_alloc_pointer &&
|
|
s <= last_alloc_size &&
|
|
a <= last_alloc_align;
|
|
}
|
|
|
|
bool checkAllocAtLeast(void* p, size_t s) const {
|
|
return p == last_alloc_pointer &&
|
|
s <= last_alloc_size;
|
|
}
|
|
|
|
bool checkDealloc(void* p, size_t s, size_t a) const {
|
|
return p == last_dealloc_pointer &&
|
|
s == last_dealloc_size &&
|
|
a == last_dealloc_align;
|
|
}
|
|
|
|
bool checkDealloc(void* p, size_t s) const {
|
|
return p == last_dealloc_pointer &&
|
|
s == last_dealloc_size;
|
|
}
|
|
|
|
bool checkDeallocMatchesAlloc() const {
|
|
return last_dealloc_pointer == last_alloc_pointer &&
|
|
last_dealloc_size == last_alloc_size &&
|
|
last_dealloc_align == last_alloc_align;
|
|
}
|
|
|
|
template <class ...Args, class Alloc, class Tp>
|
|
bool checkConstruct(Alloc const&, Tp *p) const {
|
|
auto expectAlloc = &makeTypeID<Alloc>();
|
|
auto expectTp = &makeTypeID<Tp>();
|
|
auto expectArgs = &makeArgumentID<Args...>();
|
|
if (last_construct_pointer != p)
|
|
return false;
|
|
if (last_construct_alloc != expectAlloc)
|
|
return false;
|
|
if (last_construct_type != expectTp)
|
|
return false;
|
|
if (last_construct_args != expectArgs)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
template <class Alloc, class Tp>
|
|
bool checkDestroy(Alloc const&, Tp *p) const {
|
|
return last_destroy_pointer == p &&
|
|
last_destroy_alloc == &makeTypeID<Alloc>() &&
|
|
last_destroy_type == &makeTypeID<Tp>();
|
|
}
|
|
|
|
bool checkDestroyMatchesConstruct() const {
|
|
return last_destroy_pointer == last_construct_pointer &&
|
|
last_destroy_type == last_construct_type;
|
|
}
|
|
|
|
void countIsEqual() {
|
|
++is_equal_count;
|
|
}
|
|
|
|
bool checkIsEqualCalledEq(int n) const {
|
|
return is_equal_count == n;
|
|
}
|
|
private:
|
|
DISALLOW_COPY(AllocController);
|
|
};
|
|
|
|
template <class T, size_t ID>
|
|
class CountingAllocator
|
|
{
|
|
public:
|
|
typedef T value_type;
|
|
typedef T* pointer;
|
|
|
|
template <class U>
|
|
struct rebind { using other = CountingAllocator<U, ID>; };
|
|
|
|
CountingAllocator() = delete;
|
|
explicit CountingAllocator(AllocController& PP) : P(&PP) {}
|
|
|
|
CountingAllocator(CountingAllocator const& other) : P(other.P) {
|
|
P->copy_constructed += 1;
|
|
}
|
|
|
|
CountingAllocator(CountingAllocator&& other) : P(other.P) {
|
|
P->move_constructed += 1;
|
|
}
|
|
|
|
template <class U>
|
|
CountingAllocator(CountingAllocator<U, ID> const& other) TEST_NOEXCEPT : P(other.P) {
|
|
P->copy_constructed += 1;
|
|
}
|
|
|
|
template <class U>
|
|
CountingAllocator(CountingAllocator<U, ID>&& other) TEST_NOEXCEPT : P(other.P) {
|
|
P->move_constructed += 1;
|
|
}
|
|
|
|
T* allocate(std::size_t n)
|
|
{
|
|
void* ret = ::operator new(n*sizeof(T));
|
|
P->countAlloc(ret, n*sizeof(T), alignof(T));
|
|
return static_cast<T*>(ret);
|
|
}
|
|
|
|
void deallocate(T* p, std::size_t n)
|
|
{
|
|
void* vp = static_cast<void*>(p);
|
|
P->countDealloc(vp, n*sizeof(T), alignof(T));
|
|
::operator delete(vp);
|
|
}
|
|
|
|
template <class U, class ...Args>
|
|
void construct(U *p, Args&&... args) {
|
|
::new ((void*)p) U(std::forward<Args>(args)...);
|
|
P->countConstruct<Args&&...>(*this, p);
|
|
}
|
|
|
|
template <class U>
|
|
void destroy(U* p) {
|
|
p->~U();
|
|
P->countDestroy(*this, p);
|
|
}
|
|
|
|
AllocController& getController() const { return *P; }
|
|
|
|
private:
|
|
template <class Tp, size_t XID> friend class CountingAllocator;
|
|
AllocController *P;
|
|
};
|
|
|
|
|
|
template <size_t ID>
|
|
class CountingAllocator<void, ID>
|
|
{
|
|
public:
|
|
typedef void* pointer;
|
|
typedef const void* const_pointer;
|
|
typedef void value_type;
|
|
|
|
template <class U>
|
|
struct rebind { using other = CountingAllocator<U, ID>; };
|
|
|
|
CountingAllocator() = delete;
|
|
explicit CountingAllocator(AllocController& PP) : P(&PP) {}
|
|
|
|
CountingAllocator(CountingAllocator const& other) : P(other.P) {
|
|
P->copy_constructed += 1;
|
|
}
|
|
|
|
CountingAllocator(CountingAllocator&& other) : P(other.P) {
|
|
P->move_constructed += 1;
|
|
}
|
|
|
|
template <class U>
|
|
CountingAllocator(CountingAllocator<U, ID> const& other) TEST_NOEXCEPT : P(other.P) {
|
|
P->copy_constructed += 1;
|
|
}
|
|
|
|
template <class U>
|
|
CountingAllocator(CountingAllocator<U, ID>&& other) TEST_NOEXCEPT : P(other.P) {
|
|
P->move_constructed += 1;
|
|
}
|
|
|
|
void construct(...) = delete;
|
|
void destroy(void*) = delete;
|
|
|
|
AllocController& getController() const { return *P; }
|
|
|
|
private:
|
|
template <class Tp, size_t> friend class CountingAllocator;
|
|
AllocController *P;
|
|
};
|
|
|
|
template <class T, class U, size_t ID>
|
|
inline bool operator==(CountingAllocator<T, ID> const& x,
|
|
CountingAllocator<U, ID> const& y) {
|
|
return &x.getController() == &y.getController();
|
|
}
|
|
|
|
template <class T, class U, size_t ID>
|
|
inline bool operator!=(CountingAllocator<T, ID> const& x,
|
|
CountingAllocator<U, ID> const& y) {
|
|
return !(x == y);
|
|
}
|
|
|
|
template <class T>
|
|
class MinAlignedAllocator
|
|
{
|
|
public:
|
|
typedef T value_type;
|
|
typedef T* pointer;
|
|
|
|
MinAlignedAllocator() = delete;
|
|
|
|
explicit MinAlignedAllocator(AllocController& R) : P(&R) {}
|
|
|
|
MinAlignedAllocator(MinAlignedAllocator const& other) : P(other.P) {
|
|
P->copy_constructed += 1;
|
|
}
|
|
|
|
MinAlignedAllocator(MinAlignedAllocator&& other) : P(other.P) {
|
|
P->move_constructed += 1;
|
|
}
|
|
|
|
template <class U>
|
|
MinAlignedAllocator(MinAlignedAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) {
|
|
P->copy_constructed += 1;
|
|
}
|
|
|
|
template <class U>
|
|
MinAlignedAllocator(MinAlignedAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) {
|
|
P->move_constructed += 1;
|
|
}
|
|
|
|
T* allocate(std::size_t n) {
|
|
char* aligned_ptr = (char*)::operator new(alloc_size(n*sizeof(T)));
|
|
assert(is_max_aligned(aligned_ptr));
|
|
|
|
char* unaligned_ptr = aligned_ptr + alignof(T);
|
|
assert(is_min_aligned(unaligned_ptr));
|
|
|
|
P->countAlloc(unaligned_ptr, n * sizeof(T), alignof(T));
|
|
|
|
return ((T*)unaligned_ptr);
|
|
}
|
|
|
|
void deallocate(T* p, std::size_t n) {
|
|
assert(is_min_aligned(p));
|
|
|
|
char* aligned_ptr = ((char*)p) - alignof(T);
|
|
assert(is_max_aligned(aligned_ptr));
|
|
|
|
P->countDealloc(p, n*sizeof(T), alignof(T));
|
|
|
|
return ::operator delete(static_cast<void*>(aligned_ptr));
|
|
}
|
|
|
|
template <class U, class ...Args>
|
|
void construct(U *p, Args&&... args) {
|
|
auto *c = ::new ((void*)p) U(std::forward<Args>(args)...);
|
|
P->countConstruct<Args&&...>(*this, p);
|
|
}
|
|
|
|
template <class U>
|
|
void destroy(U* p) {
|
|
p->~U();
|
|
P->countDestroy(*this, p);
|
|
}
|
|
|
|
AllocController& getController() const { return *P; }
|
|
|
|
private:
|
|
static const std::size_t BlockSize = alignof(std::max_align_t);
|
|
|
|
static std::size_t alloc_size(std::size_t s) {
|
|
std::size_t bytes = (s + BlockSize - 1) & ~(BlockSize - 1);
|
|
bytes += BlockSize;
|
|
assert(bytes % BlockSize == 0);
|
|
return bytes;
|
|
}
|
|
|
|
static bool is_max_aligned(void* p) {
|
|
return reinterpret_cast<std::uintptr_t>(p) % BlockSize == 0;
|
|
}
|
|
|
|
static bool is_min_aligned(void* p) {
|
|
if (alignof(T) == BlockSize) {
|
|
return is_max_aligned(p);
|
|
} else {
|
|
return reinterpret_cast<std::uintptr_t>(p) % BlockSize == alignof(T);
|
|
}
|
|
}
|
|
|
|
template <class Tp> friend class MinAlignedAllocator;
|
|
mutable AllocController *P;
|
|
};
|
|
|
|
|
|
template <class T, class U>
|
|
inline bool operator==(MinAlignedAllocator<T> const& x,
|
|
MinAlignedAllocator<U> const& y) {
|
|
return &x.getController() == &y.getController();
|
|
}
|
|
|
|
template <class T, class U>
|
|
inline bool operator!=(MinAlignedAllocator<T> const& x,
|
|
MinAlignedAllocator<U> const& y) {
|
|
return !(x == y);
|
|
}
|
|
|
|
template <class T>
|
|
class NullAllocator
|
|
{
|
|
public:
|
|
typedef T value_type;
|
|
typedef T* pointer;
|
|
NullAllocator() = delete;
|
|
explicit NullAllocator(AllocController& PP) : P(&PP) {}
|
|
|
|
NullAllocator(NullAllocator const& other) : P(other.P) {
|
|
P->copy_constructed += 1;
|
|
}
|
|
|
|
NullAllocator(NullAllocator&& other) : P(other.P) {
|
|
P->move_constructed += 1;
|
|
}
|
|
|
|
template <class U>
|
|
NullAllocator(NullAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) {
|
|
P->copy_constructed += 1;
|
|
}
|
|
|
|
template <class U>
|
|
NullAllocator(NullAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) {
|
|
P->move_constructed += 1;
|
|
}
|
|
|
|
T* allocate(std::size_t n)
|
|
{
|
|
P->countAlloc(nullptr, n*sizeof(T), alignof(T));
|
|
return nullptr;
|
|
}
|
|
|
|
void deallocate(T* p, std::size_t n)
|
|
{
|
|
void* vp = static_cast<void*>(p);
|
|
P->countDealloc(vp, n*sizeof(T), alignof(T));
|
|
}
|
|
|
|
AllocController& getController() const { return *P; }
|
|
|
|
private:
|
|
template <class Tp> friend class NullAllocator;
|
|
AllocController *P;
|
|
};
|
|
|
|
template <class T, class U>
|
|
inline bool operator==(NullAllocator<T> const& x,
|
|
NullAllocator<U> const& y) {
|
|
return &x.getController() == &y.getController();
|
|
}
|
|
|
|
template <class T, class U>
|
|
inline bool operator!=(NullAllocator<T> const& x,
|
|
NullAllocator<U> const& y) {
|
|
return !(x == y);
|
|
}
|
|
|
|
|
|
#endif /* SUPPORT_CONTROLLED_ALLOCATORS_H */
|