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