• 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 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