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