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 and fires zone 138 // destruction and creation events for the accounting allocator. 139 void ReleaseMemory(); 140 141 // Returns true if more memory has been allocated in zones than 142 // the limit allows. excess_allocation()143 bool excess_allocation() const { 144 return segment_bytes_allocated_ > kExcessLimit; 145 } 146 segment_bytes_allocated()147 size_t segment_bytes_allocated() const { return segment_bytes_allocated_; } 148 name()149 const char* name() const { return name_; } 150 151 // Returns precise value of used zone memory, allowed to be called only 152 // from thread owning the zone. allocation_size()153 size_t allocation_size() const { 154 size_t extra = segment_head_ ? position_ - segment_head_->start() : 0; 155 return allocation_size_ + extra; 156 } 157 158 // When V8_ENABLE_PRECISE_ZONE_STATS is not defined, returns used zone memory 159 // not including the head segment. 160 // Can be called from threads not owning the zone. allocation_size_for_tracing()161 size_t allocation_size_for_tracing() const { 162 #ifdef V8_ENABLE_PRECISE_ZONE_STATS 163 return allocation_size_for_tracing_; 164 #else 165 return allocation_size_; 166 #endif 167 } 168 169 // Returns number of bytes freed in this zone via Delete<T>()/DeleteArray<T>() 170 // calls. Returns non-zero values only when V8_ENABLE_PRECISE_ZONE_STATS is 171 // defined. freed_size_for_tracing()172 size_t freed_size_for_tracing() const { 173 #ifdef V8_ENABLE_PRECISE_ZONE_STATS 174 return freed_size_for_tracing_; 175 #else 176 return 0; 177 #endif 178 } 179 allocator()180 AccountingAllocator* allocator() const { return allocator_; } 181 182 #ifdef V8_ENABLE_PRECISE_ZONE_STATS type_stats()183 const TypeStats& type_stats() const { return type_stats_; } 184 #endif 185 186 private: 187 void* AsanNew(size_t size); 188 189 // Deletes all objects and free all memory allocated in the Zone. 190 void DeleteAll(); 191 192 // All pointers returned from New() are 8-byte aligned. 193 static const size_t kAlignmentInBytes = 8; 194 195 // Never allocate segments smaller than this size in bytes. 196 static const size_t kMinimumSegmentSize = 8 * KB; 197 198 // Never allocate segments larger than this size in bytes. 199 static const size_t kMaximumSegmentSize = 32 * KB; 200 201 // Report zone excess when allocation exceeds this limit. 202 static const size_t kExcessLimit = 256 * MB; 203 204 // The number of bytes allocated in this zone so far. 205 size_t allocation_size_ = 0; 206 207 // The number of bytes allocated in segments. Note that this number 208 // includes memory allocated from the OS but not yet allocated from 209 // the zone. 210 size_t segment_bytes_allocated_ = 0; 211 212 // Expand the Zone to hold at least 'size' more bytes and allocate 213 // the bytes. Returns the address of the newly allocated chunk of 214 // memory in the Zone. Should only be called if there isn't enough 215 // room in the Zone already. 216 Address NewExpand(size_t size); 217 218 // The free region in the current (front) segment is represented as 219 // the half-open interval [position, limit). The 'position' variable 220 // is guaranteed to be aligned as dictated by kAlignment. 221 Address position_ = 0; 222 Address limit_ = 0; 223 224 AccountingAllocator* allocator_; 225 226 Segment* segment_head_ = nullptr; 227 const char* name_; 228 const bool supports_compression_; 229 bool sealed_ = false; 230 231 #ifdef V8_ENABLE_PRECISE_ZONE_STATS 232 TypeStats type_stats_; 233 size_t allocation_size_for_tracing_ = 0; 234 235 // The number of bytes freed in this zone so far. 236 size_t freed_size_for_tracing_ = 0; 237 #endif 238 }; 239 240 // ZoneObject is an abstraction that helps define classes of objects 241 // allocated in the Zone. Use it as a base class; see ast.h. 242 class ZoneObject { 243 public: 244 // The accidential old-style pattern 245 // new (zone) SomeObject(...) 246 // now produces compilation error. The proper way of allocating objects in 247 // Zones looks like this: 248 // zone->New<SomeObject>(...) 249 void* operator new(size_t, Zone*) = delete; // See explanation above. 250 // Allow non-allocating placement new. new(size_t size,void * ptr)251 void* operator new(size_t size, void* ptr) { // See explanation above. 252 return ptr; 253 } 254 255 // Ideally, the delete operator should be private instead of 256 // public, but unfortunately the compiler sometimes synthesizes 257 // (unused) destructors for classes derived from ZoneObject, which 258 // require the operator to be visible. MSVC requires the delete 259 // operator to be public. 260 261 // ZoneObjects should never be deleted individually; use 262 // Zone::DeleteAll() to delete all zone objects in one go. 263 // Note, that descructors will not be called. delete(void *,size_t)264 void operator delete(void*, size_t) { UNREACHABLE(); } 265 void operator delete(void* pointer, Zone* zone) = delete; 266 }; 267 268 // The ZoneAllocationPolicy is used to specialize generic data 269 // structures to allocate themselves and their elements in the Zone. 270 class ZoneAllocationPolicy { 271 public: 272 // Creates unusable allocation policy. ZoneAllocationPolicy()273 ZoneAllocationPolicy() : zone_(nullptr) {} ZoneAllocationPolicy(Zone * zone)274 explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) {} 275 276 template <typename T, typename TypeTag = T[]> NewArray(size_t length)277 V8_INLINE T* NewArray(size_t length) { 278 return zone()->NewArray<T, TypeTag>(length); 279 } 280 template <typename T, typename TypeTag = T[]> DeleteArray(T * p,size_t length)281 V8_INLINE void DeleteArray(T* p, size_t length) { 282 zone()->DeleteArray<T, TypeTag>(p, length); 283 } 284 zone()285 Zone* zone() const { return zone_; } 286 287 private: 288 Zone* zone_; 289 }; 290 291 } // namespace internal 292 } // namespace v8 293 294 // The accidential old-style pattern 295 // new (zone) SomeObject(...) 296 // now produces compilation error. The proper way of allocating objects in 297 // Zones looks like this: 298 // zone->New<SomeObject>(...) 299 void* operator new(size_t, v8::internal::Zone*) = delete; // See explanation. 300 void operator delete(void*, v8::internal::Zone*) = delete; // See explanation. 301 302 #endif // V8_ZONE_ZONE_H_ 303