• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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