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/list.h"
14 #include "src/splay-tree.h"
15 #include "src/zone/accounting-allocator.h"
16 
17 #ifndef ZONE_NAME
18 #define STRINGIFY(x) #x
19 #define TOSTRING(x) STRINGIFY(x)
20 #define ZONE_NAME __FILE__ ":" TOSTRING(__LINE__)
21 #endif
22 
23 namespace v8 {
24 namespace internal {
25 
26 // The Zone supports very fast allocation of small chunks of
27 // memory. The chunks cannot be deallocated individually, but instead
28 // the Zone supports deallocating all chunks in one fast
29 // operation. The Zone is used to hold temporary data structures like
30 // the abstract syntax tree, which is deallocated after compilation.
31 //
32 // Note: There is no need to initialize the Zone; the first time an
33 // allocation is attempted, a segment of memory will be requested
34 // through the allocator.
35 //
36 // Note: The implementation is inherently not thread safe. Do not use
37 // from multi-threaded code.
38 class V8_EXPORT_PRIVATE Zone final {
39  public:
40   Zone(AccountingAllocator* allocator, const char* name);
41   ~Zone();
42 
43   // Allocate 'size' bytes of memory in the Zone; expands the Zone by
44   // allocating new segments of memory on demand using malloc().
45   void* New(size_t size);
46 
47   template <typename T>
NewArray(size_t length)48   T* NewArray(size_t length) {
49     DCHECK_LT(length, std::numeric_limits<size_t>::max() / sizeof(T));
50     return static_cast<T*>(New(length * sizeof(T)));
51   }
52 
53   // Returns true if more memory has been allocated in zones than
54   // the limit allows.
excess_allocation()55   bool excess_allocation() const {
56     return segment_bytes_allocated_ > kExcessLimit;
57   }
58 
name()59   const char* name() const { return name_; }
60 
allocation_size()61   size_t allocation_size() const { return allocation_size_; }
62 
allocator()63   AccountingAllocator* allocator() const { return allocator_; }
64 
65  private:
66 // All pointers returned from New() have this alignment.  In addition, if the
67 // object being allocated has a size that is divisible by 8 then its alignment
68 // will be 8. ASan requires 8-byte alignment. MIPS also requires 8-byte
69 // alignment.
70 #if defined(V8_USE_ADDRESS_SANITIZER) || defined(V8_TARGET_ARCH_MIPS)
71   static const size_t kAlignment = 8;
72   STATIC_ASSERT(kPointerSize <= 8);
73 #else
74   static const size_t kAlignment = kPointerSize;
75 #endif
76 
77   // Never allocate segments smaller than this size in bytes.
78   static const size_t kMinimumSegmentSize = 8 * KB;
79 
80   // Never allocate segments larger than this size in bytes.
81   static const size_t kMaximumSegmentSize = 1 * MB;
82 
83   // Report zone excess when allocation exceeds this limit.
84   static const size_t kExcessLimit = 256 * MB;
85 
86   // Deletes all objects and free all memory allocated in the Zone.
87   void DeleteAll();
88 
89   // The number of bytes allocated in this zone so far.
90   size_t allocation_size_;
91 
92   // The number of bytes allocated in segments.  Note that this number
93   // includes memory allocated from the OS but not yet allocated from
94   // the zone.
95   size_t segment_bytes_allocated_;
96 
97   // Expand the Zone to hold at least 'size' more bytes and allocate
98   // the bytes. Returns the address of the newly allocated chunk of
99   // memory in the Zone. Should only be called if there isn't enough
100   // room in the Zone already.
101   Address NewExpand(size_t size);
102 
103   // Creates a new segment, sets it size, and pushes it to the front
104   // of the segment chain. Returns the new segment.
105   inline Segment* NewSegment(size_t requested_size);
106 
107   // The free region in the current (front) segment is represented as
108   // the half-open interval [position, limit). The 'position' variable
109   // is guaranteed to be aligned as dictated by kAlignment.
110   Address position_;
111   Address limit_;
112 
113   AccountingAllocator* allocator_;
114 
115   Segment* segment_head_;
116   const char* name_;
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 // ZoneLists are growable lists with constant-time access to the
152 // elements. The list itself and all its elements are allocated in the
153 // Zone. ZoneLists cannot be deleted individually; you can delete all
154 // objects in the Zone by calling Zone::DeleteAll().
155 template <typename T>
156 class ZoneList final : public List<T, ZoneAllocationPolicy> {
157  public:
158   // Construct a new ZoneList with the given capacity; the length is
159   // always zero. The capacity must be non-negative.
ZoneList(int capacity,Zone * zone)160   ZoneList(int capacity, Zone* zone)
161       : List<T, ZoneAllocationPolicy>(capacity, ZoneAllocationPolicy(zone)) {}
162 
163   // Construct a new ZoneList from a std::initializer_list
ZoneList(std::initializer_list<T> list,Zone * zone)164   ZoneList(std::initializer_list<T> list, Zone* zone)
165       : List<T, ZoneAllocationPolicy>(static_cast<int>(list.size()),
166                                       ZoneAllocationPolicy(zone)) {
167     for (auto& i : list) Add(i, zone);
168   }
169 
new(size_t size,Zone * zone)170   void* operator new(size_t size, Zone* zone) { return zone->New(size); }
171 
172   // Construct a new ZoneList by copying the elements of the given ZoneList.
ZoneList(const ZoneList<T> & other,Zone * zone)173   ZoneList(const ZoneList<T>& other, Zone* zone)
174       : List<T, ZoneAllocationPolicy>(other.length(),
175                                       ZoneAllocationPolicy(zone)) {
176     AddAll(other, zone);
177   }
178 
179   // We add some convenience wrappers so that we can pass in a Zone
180   // instead of a (less convenient) ZoneAllocationPolicy.
Add(const T & element,Zone * zone)181   void Add(const T& element, Zone* zone) {
182     List<T, ZoneAllocationPolicy>::Add(element, ZoneAllocationPolicy(zone));
183   }
AddAll(const List<T,ZoneAllocationPolicy> & other,Zone * zone)184   void AddAll(const List<T, ZoneAllocationPolicy>& other, Zone* zone) {
185     List<T, ZoneAllocationPolicy>::AddAll(other, ZoneAllocationPolicy(zone));
186   }
AddAll(const Vector<T> & other,Zone * zone)187   void AddAll(const Vector<T>& other, Zone* zone) {
188     List<T, ZoneAllocationPolicy>::AddAll(other, ZoneAllocationPolicy(zone));
189   }
InsertAt(int index,const T & element,Zone * zone)190   void InsertAt(int index, const T& element, Zone* zone) {
191     List<T, ZoneAllocationPolicy>::InsertAt(index, element,
192                                             ZoneAllocationPolicy(zone));
193   }
AddBlock(T value,int count,Zone * zone)194   Vector<T> AddBlock(T value, int count, Zone* zone) {
195     return List<T, ZoneAllocationPolicy>::AddBlock(value, count,
196                                                    ZoneAllocationPolicy(zone));
197   }
Allocate(int length,Zone * zone)198   void Allocate(int length, Zone* zone) {
199     List<T, ZoneAllocationPolicy>::Allocate(length, ZoneAllocationPolicy(zone));
200   }
Initialize(int capacity,Zone * zone)201   void Initialize(int capacity, Zone* zone) {
202     List<T, ZoneAllocationPolicy>::Initialize(capacity,
203                                               ZoneAllocationPolicy(zone));
204   }
205 
delete(void * pointer)206   void operator delete(void* pointer) { UNREACHABLE(); }
delete(void * pointer,Zone * zone)207   void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
208 };
209 
210 // A zone splay tree.  The config type parameter encapsulates the
211 // different configurations of a concrete splay tree (see splay-tree.h).
212 // The tree itself and all its elements are allocated in the Zone.
213 template <typename Config>
214 class ZoneSplayTree final : public SplayTree<Config, ZoneAllocationPolicy> {
215  public:
ZoneSplayTree(Zone * zone)216   explicit ZoneSplayTree(Zone* zone)
217       : SplayTree<Config, ZoneAllocationPolicy>(ZoneAllocationPolicy(zone)) {}
~ZoneSplayTree()218   ~ZoneSplayTree() {
219     // Reset the root to avoid unneeded iteration over all tree nodes
220     // in the destructor.  For a zone-allocated tree, nodes will be
221     // freed by the Zone.
222     SplayTree<Config, ZoneAllocationPolicy>::ResetRoot();
223   }
224 
new(size_t size,Zone * zone)225   void* operator new(size_t size, Zone* zone) { return zone->New(size); }
226 
delete(void * pointer)227   void operator delete(void* pointer) { UNREACHABLE(); }
delete(void * pointer,Zone * zone)228   void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
229 };
230 
231 typedef base::PointerTemplateHashMapImpl<ZoneAllocationPolicy> ZoneHashMap;
232 
233 typedef base::CustomMatcherTemplateHashMapImpl<ZoneAllocationPolicy>
234     CustomMatcherZoneHashMap;
235 
236 }  // namespace internal
237 }  // namespace v8
238 
239 #endif  // V8_ZONE_ZONE_H_
240