/src/node/deps/v8/include/cppgc/allocation.h
Line | Count | Source |
1 | | // Copyright 2020 the V8 project authors. All rights reserved. |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. |
4 | | |
5 | | #ifndef INCLUDE_CPPGC_ALLOCATION_H_ |
6 | | #define INCLUDE_CPPGC_ALLOCATION_H_ |
7 | | |
8 | | #include <atomic> |
9 | | #include <cstddef> |
10 | | #include <cstdint> |
11 | | #include <new> |
12 | | #include <type_traits> |
13 | | #include <utility> |
14 | | |
15 | | #include "cppgc/custom-space.h" |
16 | | #include "cppgc/internal/api-constants.h" |
17 | | #include "cppgc/internal/gc-info.h" |
18 | | #include "cppgc/type-traits.h" |
19 | | #include "v8config.h" // NOLINT(build/include_directory) |
20 | | |
21 | | #if defined(__has_attribute) |
22 | | #if __has_attribute(assume_aligned) |
23 | | #define CPPGC_DEFAULT_ALIGNED \ |
24 | | __attribute__((assume_aligned(api_constants::kDefaultAlignment))) |
25 | | #define CPPGC_DOUBLE_WORD_ALIGNED \ |
26 | | __attribute__((assume_aligned(2 * api_constants::kDefaultAlignment))) |
27 | | #endif // __has_attribute(assume_aligned) |
28 | | #endif // defined(__has_attribute) |
29 | | |
30 | | #if !defined(CPPGC_DEFAULT_ALIGNED) |
31 | | #define CPPGC_DEFAULT_ALIGNED |
32 | | #endif |
33 | | |
34 | | #if !defined(CPPGC_DOUBLE_WORD_ALIGNED) |
35 | | #define CPPGC_DOUBLE_WORD_ALIGNED |
36 | | #endif |
37 | | |
38 | | namespace cppgc { |
39 | | |
40 | | /** |
41 | | * AllocationHandle is used to allocate garbage-collected objects. |
42 | | */ |
43 | | class AllocationHandle; |
44 | | |
45 | | namespace internal { |
46 | | |
47 | | using AlignVal = std::align_val_t; |
48 | | |
49 | | class MakeGarbageCollectedTraitInternal { |
50 | | protected: |
51 | 0 | static inline void MarkObjectAsFullyConstructed(const void* payload) { |
52 | | // See api_constants for an explanation of the constants. |
53 | 0 | std::atomic<uint16_t>* atomic_mutable_bitfield = |
54 | 0 | reinterpret_cast<std::atomic<uint16_t>*>( |
55 | 0 | const_cast<uint16_t*>(reinterpret_cast<const uint16_t*>( |
56 | 0 | reinterpret_cast<const uint8_t*>(payload) - |
57 | 0 | api_constants::kFullyConstructedBitFieldOffsetFromPayload))); |
58 | | // It's safe to split use load+store here (instead of a read-modify-write |
59 | | // operation), since it's guaranteed that this 16-bit bitfield is only |
60 | | // modified by a single thread. This is cheaper in terms of code bloat (on |
61 | | // ARM) and performance. |
62 | 0 | uint16_t value = atomic_mutable_bitfield->load(std::memory_order_relaxed); |
63 | 0 | value |= api_constants::kFullyConstructedBitMask; |
64 | 0 | atomic_mutable_bitfield->store(value, std::memory_order_release); |
65 | 0 | } |
66 | | |
67 | | // Dispatch based on compile-time information. |
68 | | // |
69 | | // Default implementation is for a custom space with >`kDefaultAlignment` byte |
70 | | // alignment. |
71 | | template <typename GCInfoType, typename CustomSpace, size_t alignment> |
72 | | struct AllocationDispatcher final { |
73 | | static void* Invoke(AllocationHandle& handle, size_t size) { |
74 | | static_assert(std::is_base_of_v<CustomSpaceBase, CustomSpace>, |
75 | | "Custom space must inherit from CustomSpaceBase."); |
76 | | static_assert( |
77 | | !CustomSpace::kSupportsCompaction, |
78 | | "Custom spaces that support compaction do not support allocating " |
79 | | "objects with non-default (i.e. word-sized) alignment."); |
80 | | return MakeGarbageCollectedTraitInternal::Allocate( |
81 | | handle, size, static_cast<AlignVal>(alignment), |
82 | | internal::GCInfoTrait<GCInfoType>::Index(), CustomSpace::kSpaceIndex); |
83 | | } |
84 | | }; |
85 | | |
86 | | // Fast path for regular allocations for the default space with |
87 | | // `kDefaultAlignment` byte alignment. |
88 | | template <typename GCInfoType> |
89 | | struct AllocationDispatcher<GCInfoType, void, |
90 | | api_constants::kDefaultAlignment> |
91 | | final { |
92 | 0 | static void* Invoke(AllocationHandle& handle, size_t size) { |
93 | 0 | return MakeGarbageCollectedTraitInternal::Allocate( |
94 | 0 | handle, size, internal::GCInfoTrait<GCInfoType>::Index()); |
95 | 0 | } |
96 | | }; |
97 | | |
98 | | // Default space with >`kDefaultAlignment` byte alignment. |
99 | | template <typename GCInfoType, size_t alignment> |
100 | | struct AllocationDispatcher<GCInfoType, void, alignment> final { |
101 | | static void* Invoke(AllocationHandle& handle, size_t size) { |
102 | | return MakeGarbageCollectedTraitInternal::Allocate( |
103 | | handle, size, static_cast<AlignVal>(alignment), |
104 | | internal::GCInfoTrait<GCInfoType>::Index()); |
105 | | } |
106 | | }; |
107 | | |
108 | | // Custom space with `kDefaultAlignment` byte alignment. |
109 | | template <typename GCInfoType, typename CustomSpace> |
110 | | struct AllocationDispatcher<GCInfoType, CustomSpace, |
111 | | api_constants::kDefaultAlignment> |
112 | | final { |
113 | | static void* Invoke(AllocationHandle& handle, size_t size) { |
114 | | static_assert(std::is_base_of_v<CustomSpaceBase, CustomSpace>, |
115 | | "Custom space must inherit from CustomSpaceBase."); |
116 | | return MakeGarbageCollectedTraitInternal::Allocate( |
117 | | handle, size, internal::GCInfoTrait<GCInfoType>::Index(), |
118 | | CustomSpace::kSpaceIndex); |
119 | | } |
120 | | }; |
121 | | |
122 | | private: |
123 | | V8_EXPORT static void* CPPGC_DEFAULT_ALIGNED |
124 | | Allocate(cppgc::AllocationHandle&, size_t, GCInfoIndex); |
125 | | V8_EXPORT static void* CPPGC_DOUBLE_WORD_ALIGNED |
126 | | Allocate(cppgc::AllocationHandle&, size_t, AlignVal, GCInfoIndex); |
127 | | V8_EXPORT static void* CPPGC_DEFAULT_ALIGNED |
128 | | Allocate(cppgc::AllocationHandle&, size_t, GCInfoIndex, CustomSpaceIndex); |
129 | | V8_EXPORT static void* CPPGC_DOUBLE_WORD_ALIGNED |
130 | | Allocate(cppgc::AllocationHandle&, size_t, AlignVal, GCInfoIndex, |
131 | | CustomSpaceIndex); |
132 | | |
133 | | friend class HeapObjectHeader; |
134 | | }; |
135 | | |
136 | | } // namespace internal |
137 | | |
138 | | /** |
139 | | * Base trait that provides utilities for advancers users that have custom |
140 | | * allocation needs (e.g., overriding size). It's expected that users override |
141 | | * MakeGarbageCollectedTrait (see below) and inherit from |
142 | | * MakeGarbageCollectedTraitBase and make use of the low-level primitives |
143 | | * offered to allocate and construct an object. |
144 | | */ |
145 | | template <typename T> |
146 | | class MakeGarbageCollectedTraitBase |
147 | | : private internal::MakeGarbageCollectedTraitInternal { |
148 | | private: |
149 | | static_assert(internal::IsGarbageCollectedType<T>::value, |
150 | | "T needs to be a garbage collected object"); |
151 | | static_assert(!IsGarbageCollectedWithMixinTypeV<T> || |
152 | | sizeof(T) <= |
153 | | internal::api_constants::kLargeObjectSizeThreshold, |
154 | | "GarbageCollectedMixin may not be a large object"); |
155 | | |
156 | | protected: |
157 | | /** |
158 | | * Allocates memory for an object of type T. |
159 | | * |
160 | | * \param handle AllocationHandle identifying the heap to allocate the object |
161 | | * on. |
162 | | * \param size The size that should be reserved for the object. |
163 | | * \returns the memory to construct an object of type T on. |
164 | | */ |
165 | 0 | V8_INLINE static void* Allocate(AllocationHandle& handle, size_t size) { |
166 | 0 | static_assert( |
167 | 0 | std::is_base_of_v<typename T::ParentMostGarbageCollectedType, T>, |
168 | 0 | "U of GarbageCollected<U> must be a base of T. Check " |
169 | 0 | "GarbageCollected<T> base class inheritance."); |
170 | 0 | static constexpr size_t kWantedAlignment = |
171 | 0 | alignof(T) < internal::api_constants::kDefaultAlignment |
172 | 0 | ? internal::api_constants::kDefaultAlignment |
173 | 0 | : alignof(T); |
174 | 0 | static_assert( |
175 | 0 | kWantedAlignment <= internal::api_constants::kMaxSupportedAlignment, |
176 | 0 | "Requested alignment larger than alignof(std::max_align_t) bytes. " |
177 | 0 | "Please file a bug to possibly get this restriction lifted."); |
178 | 0 | return AllocationDispatcher< |
179 | 0 | typename internal::GCInfoFolding< |
180 | 0 | T, typename T::ParentMostGarbageCollectedType>::ResultType, |
181 | 0 | typename SpaceTrait<T>::Space, kWantedAlignment>::Invoke(handle, size); |
182 | 0 | } Unexecuted instantiation: cppgc::MakeGarbageCollectedTraitBase<node::contextify::ContextifyContext>::Allocate(cppgc::AllocationHandle&, unsigned long) Unexecuted instantiation: cppgc::MakeGarbageCollectedTraitBase<node::contextify::ContextifyScript>::Allocate(cppgc::AllocationHandle&, unsigned long) |
183 | | |
184 | | /** |
185 | | * Marks an object as fully constructed, resulting in precise handling by the |
186 | | * garbage collector. |
187 | | * |
188 | | * \param payload The base pointer the object is allocated at. |
189 | | */ |
190 | 0 | V8_INLINE static void MarkObjectAsFullyConstructed(const void* payload) { |
191 | 0 | internal::MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed( |
192 | 0 | payload); |
193 | 0 | } Unexecuted instantiation: cppgc::MakeGarbageCollectedTraitBase<node::contextify::ContextifyContext>::MarkObjectAsFullyConstructed(void const*) Unexecuted instantiation: cppgc::MakeGarbageCollectedTraitBase<node::contextify::ContextifyScript>::MarkObjectAsFullyConstructed(void const*) |
194 | | }; |
195 | | |
196 | | /** |
197 | | * Passed to MakeGarbageCollected to specify how many bytes should be appended |
198 | | * to the allocated object. |
199 | | * |
200 | | * Example: |
201 | | * \code |
202 | | * class InlinedArray final : public GarbageCollected<InlinedArray> { |
203 | | * public: |
204 | | * explicit InlinedArray(size_t bytes) : size(bytes), byte_array(this + 1) {} |
205 | | * void Trace(Visitor*) const {} |
206 | | |
207 | | * size_t size; |
208 | | * char* byte_array; |
209 | | * }; |
210 | | * |
211 | | * auto* inlined_array = MakeGarbageCollected<InlinedArray( |
212 | | * GetAllocationHandle(), AdditionalBytes(4), 4); |
213 | | * for (size_t i = 0; i < 4; i++) { |
214 | | * Process(inlined_array->byte_array[i]); |
215 | | * } |
216 | | * \endcode |
217 | | */ |
218 | | struct AdditionalBytes { |
219 | 0 | constexpr explicit AdditionalBytes(size_t bytes) : value(bytes) {} |
220 | | const size_t value; |
221 | | }; |
222 | | |
223 | | /** |
224 | | * Default trait class that specifies how to construct an object of type T. |
225 | | * Advanced users may override how an object is constructed using the utilities |
226 | | * that are provided through MakeGarbageCollectedTraitBase. |
227 | | * |
228 | | * Any trait overriding construction must |
229 | | * - allocate through `MakeGarbageCollectedTraitBase<T>::Allocate`; |
230 | | * - mark the object as fully constructed using |
231 | | * `MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed`; |
232 | | */ |
233 | | template <typename T> |
234 | | class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase<T> { |
235 | | public: |
236 | | template <typename... Args> |
237 | 0 | static T* Call(AllocationHandle& handle, Args&&... args) { |
238 | 0 | void* memory = |
239 | 0 | MakeGarbageCollectedTraitBase<T>::Allocate(handle, sizeof(T)); |
240 | 0 | T* object = ::new (memory) T(std::forward<Args>(args)...); |
241 | 0 | MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object); |
242 | 0 | return object; |
243 | 0 | } Unexecuted instantiation: node::contextify::ContextifyContext* cppgc::MakeGarbageCollectedTrait<node::contextify::ContextifyContext>::Call<node::Environment*&, v8::Local<v8::Object>&, v8::Local<v8::Context>&, node::contextify::ContextOptions*&>(cppgc::AllocationHandle&, node::Environment*&, v8::Local<v8::Object>&, v8::Local<v8::Context>&, node::contextify::ContextOptions*&) Unexecuted instantiation: node::contextify::ContextifyScript* cppgc::MakeGarbageCollectedTrait<node::contextify::ContextifyScript>::Call<node::Environment*&, v8::Local<v8::Object>&>(cppgc::AllocationHandle&, node::Environment*&, v8::Local<v8::Object>&) |
244 | | |
245 | | template <typename... Args> |
246 | | static T* Call(AllocationHandle& handle, AdditionalBytes additional_bytes, |
247 | | Args&&... args) { |
248 | | void* memory = MakeGarbageCollectedTraitBase<T>::Allocate( |
249 | | handle, sizeof(T) + additional_bytes.value); |
250 | | T* object = ::new (memory) T(std::forward<Args>(args)...); |
251 | | MakeGarbageCollectedTraitBase<T>::MarkObjectAsFullyConstructed(object); |
252 | | return object; |
253 | | } |
254 | | }; |
255 | | |
256 | | /** |
257 | | * Allows users to specify a post-construction callback for specific types. The |
258 | | * callback is invoked on the instance of type T right after it has been |
259 | | * constructed. This can be useful when the callback requires a |
260 | | * fully-constructed object to be able to dispatch to virtual methods. |
261 | | */ |
262 | | template <typename T, typename = void> |
263 | | struct PostConstructionCallbackTrait { |
264 | 0 | static void Call(T*) {}Unexecuted instantiation: cppgc::PostConstructionCallbackTrait<node::contextify::ContextifyContext, void>::Call(node::contextify::ContextifyContext*) Unexecuted instantiation: cppgc::PostConstructionCallbackTrait<node::contextify::ContextifyScript, void>::Call(node::contextify::ContextifyScript*) |
265 | | }; |
266 | | |
267 | | /** |
268 | | * Constructs a managed object of type T where T transitively inherits from |
269 | | * GarbageCollected. |
270 | | * |
271 | | * \param args List of arguments with which an instance of T will be |
272 | | * constructed. |
273 | | * \returns an instance of type T. |
274 | | */ |
275 | | template <typename T, typename... Args> |
276 | 0 | V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, Args&&... args) { |
277 | 0 | T* object = |
278 | 0 | MakeGarbageCollectedTrait<T>::Call(handle, std::forward<Args>(args)...); |
279 | 0 | PostConstructionCallbackTrait<T>::Call(object); |
280 | 0 | return object; |
281 | 0 | } Unexecuted instantiation: node::contextify::ContextifyContext* cppgc::MakeGarbageCollected<node::contextify::ContextifyContext, node::Environment*&, v8::Local<v8::Object>&, v8::Local<v8::Context>&, node::contextify::ContextOptions*&>(cppgc::AllocationHandle&, node::Environment*&, v8::Local<v8::Object>&, v8::Local<v8::Context>&, node::contextify::ContextOptions*&) Unexecuted instantiation: node::contextify::ContextifyScript* cppgc::MakeGarbageCollected<node::contextify::ContextifyScript, node::Environment*&, v8::Local<v8::Object>&>(cppgc::AllocationHandle&, node::Environment*&, v8::Local<v8::Object>&) |
282 | | |
283 | | /** |
284 | | * Constructs a managed object of type T where T transitively inherits from |
285 | | * GarbageCollected. Created objects will have additional bytes appended to |
286 | | * it. Allocated memory would suffice for `sizeof(T) + additional_bytes`. |
287 | | * |
288 | | * \param additional_bytes Denotes how many bytes to append to T. |
289 | | * \param args List of arguments with which an instance of T will be |
290 | | * constructed. |
291 | | * \returns an instance of type T. |
292 | | */ |
293 | | template <typename T, typename... Args> |
294 | | V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, |
295 | | AdditionalBytes additional_bytes, |
296 | | Args&&... args) { |
297 | | T* object = MakeGarbageCollectedTrait<T>::Call(handle, additional_bytes, |
298 | | std::forward<Args>(args)...); |
299 | | PostConstructionCallbackTrait<T>::Call(object); |
300 | | return object; |
301 | | } |
302 | | |
303 | | } // namespace cppgc |
304 | | |
305 | | #undef CPPGC_DEFAULT_ALIGNED |
306 | | #undef CPPGC_DOUBLE_WORD_ALIGNED |
307 | | |
308 | | #endif // INCLUDE_CPPGC_ALLOCATION_H_ |