1 // Copyright 2012 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 V8_ZONE_ZONE_H_ 6 #define V8_ZONE_ZONE_H_ 7 8 #include <limits> 9 10 #include "src/base/logging.h" 11 #include "src/common/globals.h" 12 #include "src/utils/utils.h" 13 #include "src/zone/accounting-allocator.h" 14 #include "src/zone/type-stats.h" 15 #include "src/zone/zone-segment.h" 16 #include "src/zone/zone-type-traits.h" 17 18 #ifndef ZONE_NAME 19 #define ZONE_NAME __func__ 20 #endif 21 22 namespace v8 { 23 namespace internal { 24 25 // The Zone supports very fast allocation of small chunks of 26 // memory. The chunks cannot be deallocated individually, but instead 27 // the Zone supports deallocating all chunks in one fast 28 // operation. The Zone is used to hold temporary data structures like 29 // the abstract syntax tree, which is deallocated after compilation. 30 // 31 // Note: There is no need to initialize the Zone; the first time an 32 // allocation is attempted, a segment of memory will be requested 33 // through the allocator. 34 // 35 // Note: The implementation is inherently not thread safe. Do not use 36 // from multi-threaded code. 37 38 class V8_EXPORT_PRIVATE Zone final { 39 public: 40 Zone(AccountingAllocator* allocator, const char* name, 41 bool support_compression = false); 42 ~Zone(); 43 44 // Returns true if the zone supports zone pointer compression. supports_compression()45 bool supports_compression() const { 46 return COMPRESS_ZONES_BOOL && supports_compression_; 47 } 48 49 // Allocate 'size' bytes of uninitialized memory in the Zone; expands the Zone 50 // by allocating new segments of memory on demand using AccountingAllocator 51 // (see AccountingAllocator::AllocateSegment()). 52 // 53 // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the allocated bytes are 54 // associated with the provided TypeTag type. 55 template <typename TypeTag> Allocate(size_t size)56 void* Allocate(size_t size) { 57 #ifdef V8_USE_ADDRESS_SANITIZER 58 return AsanNew(size); 59 #else 60 size = RoundUp(size, kAlignmentInBytes); 61 #ifdef V8_ENABLE_PRECISE_ZONE_STATS 62 if (V8_UNLIKELY(TracingFlags::is_zone_stats_enabled())) { 63 type_stats_.AddAllocated<TypeTag>(size); 64 } 65 allocation_size_for_tracing_ += size; 66 #endif 67 Address result = position_; 68 if (V8_UNLIKELY(size > limit_ - position_)) { 69 result = NewExpand(size); 70 } else { 71 position_ += size; 72 } 73 return reinterpret_cast<void*>(result); 74 #endif // V8_USE_ADDRESS_SANITIZER 75 } 76 77 // Return 'size' bytes of memory back to Zone. These bytes can be reused 78 // for following allocations. 79 // 80 // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the deallocated bytes are 81 // associated with the provided TypeTag type. 82 template <typename TypeTag = void> Delete(void * pointer,size_t size)83 void Delete(void* pointer, size_t size) { 84 DCHECK_NOT_NULL(pointer); 85 DCHECK_NE(size, 0); 86 size = RoundUp(size, kAlignmentInBytes); 87 #ifdef V8_ENABLE_PRECISE_ZONE_STATS 88 if (V8_UNLIKELY(TracingFlags::is_zone_stats_enabled())) { 89 type_stats_.AddDeallocated<TypeTag>(size); 90 } 91 freed_size_for_tracing_ += size; 92 #endif 93 94 #ifdef DEBUG 95 static const unsigned char kZapDeadByte = 0xcd; 96 memset(pointer, kZapDeadByte, size); 97 #endif 98 } 99 100 // Allocates memory for T instance and constructs object by calling respective 101 // Args... constructor. 102 // 103 // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the allocated bytes are 104 // associated with the T type. 105 template <typename T, typename... Args> New(Args &&...args)106 T* New(Args&&... args) { 107 void* memory = Allocate<T>(sizeof(T)); 108 return new (memory) T(std::forward<Args>(args)...); 109 } 110 111 // Allocates uninitialized memory for 'length' number of T instances. 112 // 113 // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the allocated bytes are 114 // associated with the provided TypeTag type. It might be useful to tag 115 // buffer allocations with meaningful names to make buffer allocation sites 116 // distinguishable between each other. 117 template <typename T, typename TypeTag = T[]> NewArray(size_t length)118 T* NewArray(size_t length) { 119 DCHECK_IMPLIES(is_compressed_pointer<T>::value, supports_compression()); 120 DCHECK_LT(length, std::numeric_limits<size_t>::max() / sizeof(T)); 121 return static_cast<T*>(Allocate<TypeTag>(length * sizeof(T))); 122 } 123 124 // Return array of 'length' elements back to Zone. These bytes can be reused 125 // for following allocations. 126 // 127 // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the deallocated bytes are 128 // associated with the provided TypeTag type. 129 template <typename T, typename TypeTag = T[]> DeleteArray(T * pointer,size_t length)130 void DeleteArray(T* pointer, size_t length) { 131 Delete<TypeTag>(pointer, length * sizeof(T)); 132 } 133 134 // Seals the zone to prevent any further allocation. Seal()135 void Seal() { sealed_ = true; } 136 137 // Allows the zone to be safely reused. Releases the memory except for the 138 // last page, and fires zone destruction and creation events for the 139 // accounting allocator. 140 void Reset(); 141 segment_bytes_allocated()142 size_t segment_bytes_allocated() const { return segment_bytes_allocated_; } 143 name()144 const char* name() const { return name_; } 145 146 // Returns precise value of used zone memory, allowed to be called only 147 // from thread owning the zone. allocation_size()148 size_t allocation_size() const { 149 size_t extra = segment_head_ ? position_ - segment_head_->start() : 0; 150 return allocation_size_ + extra; 151 } 152 153 // When V8_ENABLE_PRECISE_ZONE_STATS is not defined, returns used zone memory 154 // not including the head segment. 155 // Can be called from threads not owning the zone. allocation_size_for_tracing()156 size_t allocation_size_for_tracing() const { 157 #ifdef V8_ENABLE_PRECISE_ZONE_STATS 158 return allocation_size_for_tracing_; 159 #else 160 return allocation_size_; 161 #endif 162 } 163 164 // Returns number of bytes freed in this zone via Delete<T>()/DeleteArray<T>() 165 // calls. Returns non-zero values only when V8_ENABLE_PRECISE_ZONE_STATS is 166 // defined. freed_size_for_tracing()167 size_t freed_size_for_tracing() const { 168 #ifdef V8_ENABLE_PRECISE_ZONE_STATS 169 return freed_size_for_tracing_; 170 #else 171 return 0; 172 #endif 173 } 174 allocator()175 AccountingAllocator* allocator() const { return allocator_; } 176 177 #ifdef V8_ENABLE_PRECISE_ZONE_STATS type_stats()178 const TypeStats& type_stats() const { return type_stats_; } 179 #endif 180 181 private: 182 void* AsanNew(size_t size); 183 184 // Deletes all objects and free all memory allocated in the Zone. 185 void DeleteAll(); 186 187 // Releases the current segment without performing any local bookkeeping 188 // (e.g. tracking allocated bytes, maintaining linked lists, etc). 189 void ReleaseSegment(Segment* segment); 190 191 // All pointers returned from New() are 8-byte aligned. 192 static const size_t kAlignmentInBytes = 8; 193 194 // Never allocate segments smaller than this size in bytes. 195 static const size_t kMinimumSegmentSize = 8 * KB; 196 197 // Never allocate segments larger than this size in bytes. 198 static const size_t kMaximumSegmentSize = 32 * KB; 199 200 // The number of bytes allocated in this zone so far. 201 std::atomic<size_t> allocation_size_ = {0}; 202 203 // The number of bytes allocated in segments. Note that this number 204 // includes memory allocated from the OS but not yet allocated from 205 // the zone. 206 std::atomic<size_t> segment_bytes_allocated_ = {0}; 207 208 // Expand the Zone to hold at least 'size' more bytes and allocate 209 // the bytes. Returns the address of the newly allocated chunk of 210 // memory in the Zone. Should only be called if there isn't enough 211 // room in the Zone already. 212 Address NewExpand(size_t size); 213 214 // The free region in the current (front) segment is represented as 215 // the half-open interval [position, limit). The 'position' variable 216 // is guaranteed to be aligned as dictated by kAlignment. 217 Address position_ = 0; 218 Address limit_ = 0; 219 220 AccountingAllocator* allocator_; 221 222 Segment* segment_head_ = nullptr; 223 const char* name_; 224 const bool supports_compression_; 225 bool sealed_ = false; 226 227 #ifdef V8_ENABLE_PRECISE_ZONE_STATS 228 TypeStats type_stats_; 229 std::atomic<size_t> allocation_size_for_tracing_ = {0}; 230 231 // The number of bytes freed in this zone so far. 232 stdd::atomic<size_t> freed_size_for_tracing_ = {0}; 233 #endif 234 235 friend class ZoneScope; 236 }; 237 238 // Similar to the HandleScope, the ZoneScope defines a region of validity for 239 // zone memory. All memory allocated in the given Zone during the scope's 240 // lifetime is freed when the scope is destructed, i.e. the Zone is reset to 241 // the state it was in when the scope was created. 242 class ZoneScope final { 243 public: 244 explicit ZoneScope(Zone* zone); 245 ~ZoneScope(); 246 247 private: 248 Zone* const zone_; 249 #ifdef V8_ENABLE_PRECISE_ZONE_STATS 250 const size_t allocation_size_for_tracing_; 251 const size_t freed_size_for_tracing_; 252 #endif 253 const size_t allocation_size_; 254 const size_t segment_bytes_allocated_; 255 const Address position_; 256 const Address limit_; 257 Segment* const segment_head_; 258 }; 259 260 // ZoneObject is an abstraction that helps define classes of objects 261 // allocated in the Zone. Use it as a base class; see ast.h. 262 class ZoneObject { 263 public: 264 // The accidential old-style pattern 265 // new (zone) SomeObject(...) 266 // now produces compilation error. The proper way of allocating objects in 267 // Zones looks like this: 268 // zone->New<SomeObject>(...) 269 void* operator new(size_t, Zone*) = delete; // See explanation above. 270 // Allow non-allocating placement new. new(size_t size,void * ptr)271 void* operator new(size_t size, void* ptr) { // See explanation above. 272 return ptr; 273 } 274 275 // Ideally, the delete operator should be private instead of 276 // public, but unfortunately the compiler sometimes synthesizes 277 // (unused) destructors for classes derived from ZoneObject, which 278 // require the operator to be visible. MSVC requires the delete 279 // operator to be public. 280 281 // ZoneObjects should never be deleted individually; use 282 // Zone::DeleteAll() to delete all zone objects in one go. 283 // Note, that descructors will not be called. delete(void *,size_t)284 void operator delete(void*, size_t) { UNREACHABLE(); } 285 void operator delete(void* pointer, Zone* zone) = delete; 286 }; 287 288 // The ZoneAllocationPolicy is used to specialize generic data 289 // structures to allocate themselves and their elements in the Zone. 290 class ZoneAllocationPolicy { 291 public: 292 // Creates unusable allocation policy. ZoneAllocationPolicy()293 ZoneAllocationPolicy() : zone_(nullptr) {} ZoneAllocationPolicy(Zone * zone)294 explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) {} 295 296 template <typename T, typename TypeTag = T[]> NewArray(size_t length)297 V8_INLINE T* NewArray(size_t length) { 298 return zone()->NewArray<T, TypeTag>(length); 299 } 300 template <typename T, typename TypeTag = T[]> DeleteArray(T * p,size_t length)301 V8_INLINE void DeleteArray(T* p, size_t length) { 302 zone()->DeleteArray<T, TypeTag>(p, length); 303 } 304 zone()305 Zone* zone() const { return zone_; } 306 307 private: 308 Zone* zone_; 309 }; 310 311 } // namespace internal 312 } // namespace v8 313 314 // The accidential old-style pattern 315 // new (zone) SomeObject(...) 316 // now produces compilation error. The proper way of allocating objects in 317 // Zones looks like this: 318 // zone->New<SomeObject>(...) 319 void* operator new(size_t, v8::internal::Zone*) = delete; // See explanation. 320 void operator delete(void*, v8::internal::Zone*) = delete; // See explanation. 321 322 #endif // V8_ZONE_ZONE_H_ 323