Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/Stockfish
Path: blob/master/src/memory.h
376 views
1
/*
2
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
4
5
Stockfish is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Stockfish is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#ifndef MEMORY_H_INCLUDED
20
#define MEMORY_H_INCLUDED
21
22
#include <algorithm>
23
#include <cstddef>
24
#include <cstdint>
25
#include <memory>
26
#include <new>
27
#include <type_traits>
28
#include <utility>
29
30
#include "types.h"
31
32
namespace Stockfish {
33
34
void* std_aligned_alloc(size_t alignment, size_t size);
35
void std_aligned_free(void* ptr);
36
37
// Memory aligned by page size, min alignment: 4096 bytes
38
void* aligned_large_pages_alloc(size_t size);
39
void aligned_large_pages_free(void* mem);
40
41
bool has_large_pages();
42
43
// Frees memory which was placed there with placement new.
44
// Works for both single objects and arrays of unknown bound.
45
template<typename T, typename FREE_FUNC>
46
void memory_deleter(T* ptr, FREE_FUNC free_func) {
47
if (!ptr)
48
return;
49
50
// Explicitly needed to call the destructor
51
if constexpr (!std::is_trivially_destructible_v<T>)
52
ptr->~T();
53
54
free_func(ptr);
55
}
56
57
// Frees memory which was placed there with placement new.
58
// Works for both single objects and arrays of unknown bound.
59
template<typename T, typename FREE_FUNC>
60
void memory_deleter_array(T* ptr, FREE_FUNC free_func) {
61
if (!ptr)
62
return;
63
64
65
// Move back on the pointer to where the size is allocated
66
const size_t array_offset = std::max(sizeof(size_t), alignof(T));
67
char* raw_memory = reinterpret_cast<char*>(ptr) - array_offset;
68
69
if constexpr (!std::is_trivially_destructible_v<T>)
70
{
71
const size_t size = *reinterpret_cast<size_t*>(raw_memory);
72
73
// Explicitly call the destructor for each element in reverse order
74
for (size_t i = size; i-- > 0;)
75
ptr[i].~T();
76
}
77
78
free_func(raw_memory);
79
}
80
81
// Allocates memory for a single object and places it there with placement new
82
template<typename T, typename ALLOC_FUNC, typename... Args>
83
inline std::enable_if_t<!std::is_array_v<T>, T*> memory_allocator(ALLOC_FUNC alloc_func,
84
Args&&... args) {
85
void* raw_memory = alloc_func(sizeof(T));
86
ASSERT_ALIGNED(raw_memory, alignof(T));
87
return new (raw_memory) T(std::forward<Args>(args)...);
88
}
89
90
// Allocates memory for an array of unknown bound and places it there with placement new
91
template<typename T, typename ALLOC_FUNC>
92
inline std::enable_if_t<std::is_array_v<T>, std::remove_extent_t<T>*>
93
memory_allocator(ALLOC_FUNC alloc_func, size_t num) {
94
using ElementType = std::remove_extent_t<T>;
95
96
const size_t array_offset = std::max(sizeof(size_t), alignof(ElementType));
97
98
// Save the array size in the memory location
99
char* raw_memory =
100
reinterpret_cast<char*>(alloc_func(array_offset + num * sizeof(ElementType)));
101
ASSERT_ALIGNED(raw_memory, alignof(T));
102
103
new (raw_memory) size_t(num);
104
105
for (size_t i = 0; i < num; ++i)
106
new (raw_memory + array_offset + i * sizeof(ElementType)) ElementType();
107
108
// Need to return the pointer at the start of the array so that
109
// the indexing in unique_ptr<T[]> works.
110
return reinterpret_cast<ElementType*>(raw_memory + array_offset);
111
}
112
113
//
114
//
115
// aligned large page unique ptr
116
//
117
//
118
119
template<typename T>
120
struct LargePageDeleter {
121
void operator()(T* ptr) const { return memory_deleter<T>(ptr, aligned_large_pages_free); }
122
};
123
124
template<typename T>
125
struct LargePageArrayDeleter {
126
void operator()(T* ptr) const { return memory_deleter_array<T>(ptr, aligned_large_pages_free); }
127
};
128
129
template<typename T>
130
using LargePagePtr =
131
std::conditional_t<std::is_array_v<T>,
132
std::unique_ptr<T, LargePageArrayDeleter<std::remove_extent_t<T>>>,
133
std::unique_ptr<T, LargePageDeleter<T>>>;
134
135
// make_unique_large_page for single objects
136
template<typename T, typename... Args>
137
std::enable_if_t<!std::is_array_v<T>, LargePagePtr<T>> make_unique_large_page(Args&&... args) {
138
static_assert(alignof(T) <= 4096,
139
"aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
140
141
T* obj = memory_allocator<T>(aligned_large_pages_alloc, std::forward<Args>(args)...);
142
143
return LargePagePtr<T>(obj);
144
}
145
146
// make_unique_large_page for arrays of unknown bound
147
template<typename T>
148
std::enable_if_t<std::is_array_v<T>, LargePagePtr<T>> make_unique_large_page(size_t num) {
149
using ElementType = std::remove_extent_t<T>;
150
151
static_assert(alignof(ElementType) <= 4096,
152
"aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
153
154
ElementType* memory = memory_allocator<T>(aligned_large_pages_alloc, num);
155
156
return LargePagePtr<T>(memory);
157
}
158
159
//
160
//
161
// aligned unique ptr
162
//
163
//
164
165
template<typename T>
166
struct AlignedDeleter {
167
void operator()(T* ptr) const { return memory_deleter<T>(ptr, std_aligned_free); }
168
};
169
170
template<typename T>
171
struct AlignedArrayDeleter {
172
void operator()(T* ptr) const { return memory_deleter_array<T>(ptr, std_aligned_free); }
173
};
174
175
template<typename T>
176
using AlignedPtr =
177
std::conditional_t<std::is_array_v<T>,
178
std::unique_ptr<T, AlignedArrayDeleter<std::remove_extent_t<T>>>,
179
std::unique_ptr<T, AlignedDeleter<T>>>;
180
181
// make_unique_aligned for single objects
182
template<typename T, typename... Args>
183
std::enable_if_t<!std::is_array_v<T>, AlignedPtr<T>> make_unique_aligned(Args&&... args) {
184
const auto func = [](size_t size) { return std_aligned_alloc(alignof(T), size); };
185
T* obj = memory_allocator<T>(func, std::forward<Args>(args)...);
186
187
return AlignedPtr<T>(obj);
188
}
189
190
// make_unique_aligned for arrays of unknown bound
191
template<typename T>
192
std::enable_if_t<std::is_array_v<T>, AlignedPtr<T>> make_unique_aligned(size_t num) {
193
using ElementType = std::remove_extent_t<T>;
194
195
const auto func = [](size_t size) { return std_aligned_alloc(alignof(ElementType), size); };
196
ElementType* memory = memory_allocator<T>(func, num);
197
198
return AlignedPtr<T>(memory);
199
}
200
201
202
// Get the first aligned element of an array.
203
// ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes,
204
// where N is the number of elements in the array.
205
template<uintptr_t Alignment, typename T>
206
T* align_ptr_up(T* ptr) {
207
static_assert(alignof(T) < Alignment);
208
209
const uintptr_t ptrint = reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(ptr));
210
return reinterpret_cast<T*>(
211
reinterpret_cast<char*>((ptrint + (Alignment - 1)) / Alignment * Alignment));
212
}
213
214
215
} // namespace Stockfish
216
217
#endif // #ifndef MEMORY_H_INCLUDED
218
219