• 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/hashmap.h"
11 #include "src/base/logging.h"
12 #include "src/globals.h"
13 #include "src/splay-tree.h"
14 #include "src/zone/accounting-allocator.h"
15 
16 #ifndef ZONE_NAME
17 #define STRINGIFY(x) #x
18 #define TOSTRING(x) STRINGIFY(x)
19 #define ZONE_NAME __FILE__ ":" TOSTRING(__LINE__)
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 enum class SegmentSize { kLarge, kDefault };
39 
40 class V8_EXPORT_PRIVATE Zone final {
41  public:
42   Zone(AccountingAllocator* allocator, const char* name,
43        SegmentSize segment_size = SegmentSize::kDefault);
44   ~Zone();
45 
46   // Allocate 'size' bytes of memory in the Zone; expands the Zone by
47   // allocating new segments of memory on demand using malloc().
48   void* New(size_t size);
49 
50   template <typename T>
NewArray(size_t length)51   T* NewArray(size_t length) {
52     DCHECK_LT(length, std::numeric_limits<size_t>::max() / sizeof(T));
53     return static_cast<T*>(New(length * sizeof(T)));
54   }
55 
56   // Seals the zone to prevent any further allocation.
Seal()57   void Seal() { sealed_ = true; }
58 
59   // Returns true if more memory has been allocated in zones than
60   // the limit allows.
excess_allocation()61   bool excess_allocation() const {
62     return segment_bytes_allocated_ > kExcessLimit;
63   }
64 
name()65   const char* name() const { return name_; }
66 
allocation_size()67   size_t allocation_size() const { return allocation_size_; }
68 
allocator()69   AccountingAllocator* allocator() const { return allocator_; }
70 
71  private:
72   // All pointers returned from New() are 8-byte aligned.
73   static const size_t kAlignmentInBytes = 8;
74 
75   // Never allocate segments smaller than this size in bytes.
76   static const size_t kMinimumSegmentSize = 8 * KB;
77 
78   // Never allocate segments larger than this size in bytes.
79   static const size_t kMaximumSegmentSize = 1 * MB;
80 
81   // Report zone excess when allocation exceeds this limit.
82   static const size_t kExcessLimit = 256 * MB;
83 
84   // Deletes all objects and free all memory allocated in the Zone.
85   void DeleteAll();
86 
87   // The number of bytes allocated in this zone so far.
88   size_t allocation_size_;
89 
90   // The number of bytes allocated in segments.  Note that this number
91   // includes memory allocated from the OS but not yet allocated from
92   // the zone.
93   size_t segment_bytes_allocated_;
94 
95   // Expand the Zone to hold at least 'size' more bytes and allocate
96   // the bytes. Returns the address of the newly allocated chunk of
97   // memory in the Zone. Should only be called if there isn't enough
98   // room in the Zone already.
99   Address NewExpand(size_t size);
100 
101   // Creates a new segment, sets it size, and pushes it to the front
102   // of the segment chain. Returns the new segment.
103   inline Segment* NewSegment(size_t requested_size);
104 
105   // The free region in the current (front) segment is represented as
106   // the half-open interval [position, limit). The 'position' variable
107   // is guaranteed to be aligned as dictated by kAlignment.
108   Address position_;
109   Address limit_;
110 
111   AccountingAllocator* allocator_;
112 
113   Segment* segment_head_;
114   const char* name_;
115   bool sealed_;
116   SegmentSize segment_size_;
117 };
118 
119 // ZoneObject is an abstraction that helps define classes of objects
120 // allocated in the Zone. Use it as a base class; see ast.h.
121 class ZoneObject {
122  public:
123   // Allocate a new ZoneObject of 'size' bytes in the Zone.
new(size_t size,Zone * zone)124   void* operator new(size_t size, Zone* zone) { return zone->New(size); }
125 
126   // Ideally, the delete operator should be private instead of
127   // public, but unfortunately the compiler sometimes synthesizes
128   // (unused) destructors for classes derived from ZoneObject, which
129   // require the operator to be visible. MSVC requires the delete
130   // operator to be public.
131 
132   // ZoneObjects should never be deleted individually; use
133   // Zone::DeleteAll() to delete all zone objects in one go.
delete(void *,size_t)134   void operator delete(void*, size_t) { UNREACHABLE(); }
delete(void * pointer,Zone * zone)135   void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
136 };
137 
138 // The ZoneAllocationPolicy is used to specialize generic data
139 // structures to allocate themselves and their elements in the Zone.
140 class ZoneAllocationPolicy final {
141  public:
ZoneAllocationPolicy(Zone * zone)142   explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) {}
New(size_t size)143   void* New(size_t size) { return zone()->New(size); }
Delete(void * pointer)144   static void Delete(void* pointer) {}
zone()145   Zone* zone() const { return zone_; }
146 
147  private:
148   Zone* zone_;
149 };
150 
151 template <typename T>
152 class Vector;
153 
154 // ZoneLists are growable lists with constant-time access to the
155 // elements. The list itself and all its elements are allocated in the
156 // Zone. ZoneLists cannot be deleted individually; you can delete all
157 // objects in the Zone by calling Zone::DeleteAll().
158 template <typename T>
159 class ZoneList final {
160  public:
161   // Construct a new ZoneList with the given capacity; the length is
162   // always zero. The capacity must be non-negative.
ZoneList(int capacity,Zone * zone)163   ZoneList(int capacity, Zone* zone) { Initialize(capacity, zone); }
164   // Construct a new ZoneList from a std::initializer_list
ZoneList(std::initializer_list<T> list,Zone * zone)165   ZoneList(std::initializer_list<T> list, Zone* zone) {
166     Initialize(static_cast<int>(list.size()), zone);
167     for (auto& i : list) Add(i, zone);
168   }
169   // Construct a new ZoneList by copying the elements of the given ZoneList.
ZoneList(const ZoneList<T> & other,Zone * zone)170   ZoneList(const ZoneList<T>& other, Zone* zone) {
171     Initialize(other.length(), zone);
172     AddAll(other, zone);
173   }
174 
~ZoneList()175   V8_INLINE ~ZoneList() { DeleteData(data_); }
176 
177   // Please the MSVC compiler.  We should never have to execute this.
delete(void * p,ZoneAllocationPolicy allocator)178   V8_INLINE void operator delete(void* p, ZoneAllocationPolicy allocator) {
179     UNREACHABLE();
180   }
181 
new(size_t size,Zone * zone)182   void* operator new(size_t size, Zone* zone) { return zone->New(size); }
183 
184   // Returns a reference to the element at index i. This reference is not safe
185   // to use after operations that can change the list's backing store
186   // (e.g. Add).
187   inline T& operator[](int i) const {
188     DCHECK_LE(0, i);
189     DCHECK_GT(static_cast<unsigned>(length_), static_cast<unsigned>(i));
190     return data_[i];
191   }
at(int i)192   inline T& at(int i) const { return operator[](i); }
last()193   inline T& last() const { return at(length_ - 1); }
first()194   inline T& first() const { return at(0); }
195 
196   typedef T* iterator;
begin()197   inline iterator begin() const { return &data_[0]; }
end()198   inline iterator end() const { return &data_[length_]; }
199 
is_empty()200   V8_INLINE bool is_empty() const { return length_ == 0; }
length()201   V8_INLINE int length() const { return length_; }
capacity()202   V8_INLINE int capacity() const { return capacity_; }
203 
ToVector()204   Vector<T> ToVector() const { return Vector<T>(data_, length_); }
205 
ToConstVector()206   Vector<const T> ToConstVector() const {
207     return Vector<const T>(data_, length_);
208   }
209 
Initialize(int capacity,Zone * zone)210   V8_INLINE void Initialize(int capacity, Zone* zone) {
211     DCHECK_GE(capacity, 0);
212     data_ = (capacity > 0) ? NewData(capacity, ZoneAllocationPolicy(zone))
213                            : nullptr;
214     capacity_ = capacity;
215     length_ = 0;
216   }
217 
218   // Adds a copy of the given 'element' to the end of the list,
219   // expanding the list if necessary.
220   void Add(const T& element, Zone* zone);
221   // Add all the elements from the argument list to this list.
222   void AddAll(const ZoneList<T>& other, Zone* zone);
223   // Add all the elements from the vector to this list.
224   void AddAll(const Vector<T>& other, Zone* zone);
225   // Inserts the element at the specific index.
226   void InsertAt(int index, const T& element, Zone* zone);
227 
228   // Added 'count' elements with the value 'value' and returns a
229   // vector that allows access to the elements. The vector is valid
230   // until the next change is made to this list.
231   Vector<T> AddBlock(T value, int count, Zone* zone);
232 
233   // Overwrites the element at the specific index.
234   void Set(int index, const T& element);
235 
236   // Removes the i'th element without deleting it even if T is a
237   // pointer type; moves all elements above i "down". Returns the
238   // removed element.  This function's complexity is linear in the
239   // size of the list.
240   T Remove(int i);
241 
242   // Removes the last element without deleting it even if T is a
243   // pointer type. Returns the removed element.
RemoveLast()244   V8_INLINE T RemoveLast() { return Remove(length_ - 1); }
245 
246   // Clears the list by freeing the storage memory. If you want to keep the
247   // memory, use Rewind(0) instead. Be aware, that even if T is a
248   // pointer type, clearing the list doesn't delete the entries.
249   V8_INLINE void Clear();
250 
251   // Drops all but the first 'pos' elements from the list.
252   V8_INLINE void Rewind(int pos);
253 
254   inline bool Contains(const T& elm) const;
255 
256   // Iterate through all list entries, starting at index 0.
257   template <class Visitor>
258   void Iterate(Visitor* visitor);
259 
260   // Sort all list entries (using QuickSort)
261   template <typename CompareFunction>
262   void Sort(CompareFunction cmp);
263   template <typename CompareFunction>
264   void StableSort(CompareFunction cmp, size_t start, size_t length);
265 
delete(void * pointer)266   void operator delete(void* pointer) { UNREACHABLE(); }
delete(void * pointer,Zone * zone)267   void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
268 
269  private:
270   T* data_;
271   int capacity_;
272   int length_;
273 
NewData(int n,ZoneAllocationPolicy allocator)274   V8_INLINE T* NewData(int n, ZoneAllocationPolicy allocator) {
275     return static_cast<T*>(allocator.New(n * sizeof(T)));
276   }
DeleteData(T * data)277   V8_INLINE void DeleteData(T* data) { ZoneAllocationPolicy::Delete(data); }
278 
279   // Increase the capacity of a full list, and add an element.
280   // List must be full already.
281   void ResizeAdd(const T& element, ZoneAllocationPolicy allocator);
282 
283   // Inlined implementation of ResizeAdd, shared by inlined and
284   // non-inlined versions of ResizeAdd.
285   void ResizeAddInternal(const T& element, ZoneAllocationPolicy allocator);
286 
287   // Resize the list.
288   void Resize(int new_capacity, ZoneAllocationPolicy allocator);
289 
290   DISALLOW_COPY_AND_ASSIGN(ZoneList);
291 };
292 
293 // ZonePtrList is a ZoneList of pointers to ZoneObjects allocated in the same
294 // zone as the list object.
295 template <typename T>
296 using ZonePtrList = ZoneList<T*>;
297 
298 // A zone splay tree.  The config type parameter encapsulates the
299 // different configurations of a concrete splay tree (see splay-tree.h).
300 // The tree itself and all its elements are allocated in the Zone.
301 template <typename Config>
302 class ZoneSplayTree final : public SplayTree<Config, ZoneAllocationPolicy> {
303  public:
ZoneSplayTree(Zone * zone)304   explicit ZoneSplayTree(Zone* zone)
305       : SplayTree<Config, ZoneAllocationPolicy>(ZoneAllocationPolicy(zone)) {}
~ZoneSplayTree()306   ~ZoneSplayTree() {
307     // Reset the root to avoid unneeded iteration over all tree nodes
308     // in the destructor.  For a zone-allocated tree, nodes will be
309     // freed by the Zone.
310     SplayTree<Config, ZoneAllocationPolicy>::ResetRoot();
311   }
312 
new(size_t size,Zone * zone)313   void* operator new(size_t size, Zone* zone) { return zone->New(size); }
314 
delete(void * pointer)315   void operator delete(void* pointer) { UNREACHABLE(); }
delete(void * pointer,Zone * zone)316   void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
317 };
318 
319 typedef base::PointerTemplateHashMapImpl<ZoneAllocationPolicy> ZoneHashMap;
320 
321 typedef base::CustomMatcherTemplateHashMapImpl<ZoneAllocationPolicy>
322     CustomMatcherZoneHashMap;
323 
324 }  // namespace internal
325 }  // namespace v8
326 
327 // The accidential pattern
328 //    new (zone) SomeObject()
329 // where SomeObject does not inherit from ZoneObject leads to nasty crashes.
330 // This triggers a compile-time error instead.
331 template <class T, typename = typename std::enable_if<std::is_convertible<
332                        T, const v8::internal::Zone*>::value>::type>
new(size_t size,T zone)333 void* operator new(size_t size, T zone) {
334   static_assert(false && sizeof(T),
335                 "Placement new with a zone is only permitted for classes "
336                 "inheriting from ZoneObject");
337   UNREACHABLE();
338 }
339 
340 #endif  // V8_ZONE_ZONE_H_
341