• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
50 class V8_EXPORT MakeGarbageCollectedTraitInternal {
51  protected:
MarkObjectAsFullyConstructed(const void * payload)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 {
Invokefinal74     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,
133                                                   CustomSpaceIndex);
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>
148 class MakeGarbageCollectedTraitBase
149     : private internal::MakeGarbageCollectedTraitInternal {
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) {
193     internal::MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed(
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  */
220 struct AdditionalBytes {
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>
236 class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase<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>
265 struct PostConstructionCallbackTrait {
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_
311