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