zone.h 14.1 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5 6
#ifndef V8_ZONE_ZONE_H_
#define V8_ZONE_ZONE_H_
7

8 9
#include <limits>

lpy's avatar
lpy committed
10
#include "src/base/hashmap.h"
11
#include "src/base/logging.h"
12
#include "src/base/threaded-list.h"
13 14
#include "src/globals.h"
#include "src/splay-tree.h"
15
#include "src/utils.h"
16
#include "src/zone/accounting-allocator.h"
17

18 19 20 21 22 23
#ifndef ZONE_NAME
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define ZONE_NAME __FILE__ ":" TOSTRING(__LINE__)
#endif

24 25
namespace v8 {
namespace internal {
26 27 28 29 30 31

// The Zone supports very fast allocation of small chunks of
// memory. The chunks cannot be deallocated individually, but instead
// the Zone supports deallocating all chunks in one fast
// operation. The Zone is used to hold temporary data structures like
// the abstract syntax tree, which is deallocated after compilation.
32
//
33 34
// Note: There is no need to initialize the Zone; the first time an
// allocation is attempted, a segment of memory will be requested
35
// through the allocator.
36
//
37 38
// Note: The implementation is inherently not thread safe. Do not use
// from multi-threaded code.
39 40 41

enum class SegmentSize { kLarge, kDefault };

42
class V8_EXPORT_PRIVATE Zone final {
43
 public:
44 45
  Zone(AccountingAllocator* allocator, const char* name,
       SegmentSize segment_size = SegmentSize::kDefault);
46
  ~Zone();
47

48 49
  // Allocate 'size' bytes of memory in the Zone; expands the Zone by
  // allocating new segments of memory on demand using malloc().
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
  void* New(size_t size) {
#ifdef V8_USE_ADDRESS_SANITIZER
    return AsanNew(size);
#else
    size = RoundUp(size, kAlignmentInBytes);
    Address result = position_;
    if (V8_UNLIKELY(size > limit_ - position_)) {
      result = NewExpand(size);
    } else {
      position_ += size;
    }
    return reinterpret_cast<void*>(result);
#endif
  }
  void* AsanNew(size_t size);
65

66
  template <typename T>
67 68
  T* NewArray(size_t length) {
    DCHECK_LT(length, std::numeric_limits<size_t>::max() / sizeof(T));
69 70
    return static_cast<T*>(New(length * sizeof(T)));
  }
71

72 73 74
  // Seals the zone to prevent any further allocation.
  void Seal() { sealed_ = true; }

75 76 77
  // Allows the zone to be safely reused. Releases the memory and fires zone
  // destruction and creation events for the accounting allocator.
  void ReleaseMemory();
78

79 80
  // Returns true if more memory has been allocated in zones than
  // the limit allows.
81 82 83
  bool excess_allocation() const {
    return segment_bytes_allocated_ > kExcessLimit;
  }
84

heimbuef's avatar
heimbuef committed
85 86
  const char* name() const { return name_; }

87 88 89 90
  size_t allocation_size() const {
    size_t extra = segment_head_ ? position_ - segment_head_->start() : 0;
    return allocation_size_ + extra;
  }
91

92
  AccountingAllocator* allocator() const { return allocator_; }
93

94
 private:
95 96 97
  // Deletes all objects and free all memory allocated in the Zone.
  void DeleteAll();

98 99
  // All pointers returned from New() are 8-byte aligned.
  static const size_t kAlignmentInBytes = 8;
100 101

  // Never allocate segments smaller than this size in bytes.
102
  static const size_t kMinimumSegmentSize = 8 * KB;
iposva@chromium.org's avatar
iposva@chromium.org committed
103

104
  // Never allocate segments larger than this size in bytes.
105
  static const size_t kMaximumSegmentSize = 1 * MB;
106

107
  // Report zone excess when allocation exceeds this limit.
108
  static const size_t kExcessLimit = 256 * MB;
109

110
  // The number of bytes allocated in this zone so far.
111
  size_t allocation_size_;
112

113 114 115
  // The number of bytes allocated in segments.  Note that this number
  // includes memory allocated from the OS but not yet allocated from
  // the zone.
116
  size_t segment_bytes_allocated_;
117 118 119 120 121

  // Expand the Zone to hold at least 'size' more bytes and allocate
  // the bytes. Returns the address of the newly allocated chunk of
  // memory in the Zone. Should only be called if there isn't enough
  // room in the Zone already.
122
  Address NewExpand(size_t size);
123 124 125

  // Creates a new segment, sets it size, and pushes it to the front
  // of the segment chain. Returns the new segment.
126
  inline Segment* NewSegment(size_t requested_size);
127 128 129 130

  // The free region in the current (front) segment is represented as
  // the half-open interval [position, limit). The 'position' variable
  // is guaranteed to be aligned as dictated by kAlignment.
131 132 133
  Address position_;
  Address limit_;

134
  AccountingAllocator* allocator_;
135

136
  Segment* segment_head_;
heimbuef's avatar
heimbuef committed
137
  const char* name_;
138
  bool sealed_;
139
  SegmentSize segment_size_;
140 141 142 143 144 145 146
};

// ZoneObject is an abstraction that helps define classes of objects
// allocated in the Zone. Use it as a base class; see ast.h.
class ZoneObject {
 public:
  // Allocate a new ZoneObject of 'size' bytes in the Zone.
147
  void* operator new(size_t size, Zone* zone) { return zone->New(size); }
148 149

  // Ideally, the delete operator should be private instead of
150
  // public, but unfortunately the compiler sometimes synthesizes
151 152 153 154 155 156 157
  // (unused) destructors for classes derived from ZoneObject, which
  // require the operator to be visible. MSVC requires the delete
  // operator to be public.

  // ZoneObjects should never be deleted individually; use
  // Zone::DeleteAll() to delete all zone objects in one go.
  void operator delete(void*, size_t) { UNREACHABLE(); }
158
  void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
159 160
};

161 162
// The ZoneAllocationPolicy is used to specialize generic data
// structures to allocate themselves and their elements in the Zone.
163
class ZoneAllocationPolicy final {
164
 public:
165
  explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) {}
166
  void* New(size_t size) { return zone()->New(size); }
167 168
  static void Delete(void* pointer) {}
  Zone* zone() const { return zone_; }
169

170 171
 private:
  Zone* zone_;
172 173
};

174 175 176
template <typename T>
class Vector;

177 178 179 180
// ZoneLists are growable lists with constant-time access to the
// elements. The list itself and all its elements are allocated in the
// Zone. ZoneLists cannot be deleted individually; you can delete all
// objects in the Zone by calling Zone::DeleteAll().
181
template <typename T>
182
class ZoneList final {
183 184 185
 public:
  // Construct a new ZoneList with the given capacity; the length is
  // always zero. The capacity must be non-negative.
186
  ZoneList(int capacity, Zone* zone) { Initialize(capacity, zone); }
187
  // Construct a new ZoneList from a std::initializer_list
188 189
  ZoneList(std::initializer_list<T> list, Zone* zone) {
    Initialize(static_cast<int>(list.size()), zone);
190 191
    for (auto& i : list) Add(i, zone);
  }
192
  // Construct a new ZoneList by copying the elements of the given ZoneList.
193 194
  ZoneList(const ZoneList<T>& other, Zone* zone) {
    Initialize(other.length(), zone);
195
    AddAll(other, zone);
196 197
  }

198
  V8_INLINE ~ZoneList() { DeleteData(data_); }
199 200

  // Please the MSVC compiler.  We should never have to execute this.
201
  V8_INLINE void operator delete(void* p, ZoneAllocationPolicy allocator) {
202
    UNREACHABLE();
203
  }
204 205 206 207 208 209 210 211 212 213

  void* operator new(size_t size, Zone* zone) { return zone->New(size); }

  // Returns a reference to the element at index i. This reference is not safe
  // to use after operations that can change the list's backing store
  // (e.g. Add).
  inline T& operator[](int i) const {
    DCHECK_LE(0, i);
    DCHECK_GT(static_cast<unsigned>(length_), static_cast<unsigned>(i));
    return data_[i];
214
  }
215 216 217 218 219 220 221 222
  inline T& at(int i) const { return operator[](i); }
  inline T& last() const { return at(length_ - 1); }
  inline T& first() const { return at(0); }

  typedef T* iterator;
  inline iterator begin() const { return &data_[0]; }
  inline iterator end() const { return &data_[length_]; }

223 224 225
  V8_INLINE bool is_empty() const { return length_ == 0; }
  V8_INLINE int length() const { return length_; }
  V8_INLINE int capacity() const { return capacity_; }
226 227

  Vector<T> ToVector() const { return Vector<T>(data_, length_); }
228 229 230
  Vector<T> ToVector(int start, int length) const {
    return Vector<T>(data_ + start, Min(length_ - start, length));
  }
231 232 233

  Vector<const T> ToConstVector() const {
    return Vector<const T>(data_, length_);
234
  }
235

236
  V8_INLINE void Initialize(int capacity, Zone* zone) {
237
    DCHECK_GE(capacity, 0);
238 239
    data_ = (capacity > 0) ? NewData(capacity, ZoneAllocationPolicy(zone))
                           : nullptr;
240 241
    capacity_ = capacity;
    length_ = 0;
242
  }
243

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
  // Adds a copy of the given 'element' to the end of the list,
  // expanding the list if necessary.
  void Add(const T& element, Zone* zone);
  // Add all the elements from the argument list to this list.
  void AddAll(const ZoneList<T>& other, Zone* zone);
  // Add all the elements from the vector to this list.
  void AddAll(const Vector<T>& other, Zone* zone);
  // Inserts the element at the specific index.
  void InsertAt(int index, const T& element, Zone* zone);

  // Added 'count' elements with the value 'value' and returns a
  // vector that allows access to the elements. The vector is valid
  // until the next change is made to this list.
  Vector<T> AddBlock(T value, int count, Zone* zone);

  // Overwrites the element at the specific index.
  void Set(int index, const T& element);

  // Removes the i'th element without deleting it even if T is a
  // pointer type; moves all elements above i "down". Returns the
  // removed element.  This function's complexity is linear in the
  // size of the list.
  T Remove(int i);

  // Removes the last element without deleting it even if T is a
  // pointer type. Returns the removed element.
270
  V8_INLINE T RemoveLast() { return Remove(length_ - 1); }
271 272 273 274

  // Clears the list by freeing the storage memory. If you want to keep the
  // memory, use Rewind(0) instead. Be aware, that even if T is a
  // pointer type, clearing the list doesn't delete the entries.
275
  V8_INLINE void Clear();
276 277

  // Drops all but the first 'pos' elements from the list.
278
  V8_INLINE void Rewind(int pos);
279 280 281 282 283 284 285 286 287 288 289 290 291

  inline bool Contains(const T& elm) const;

  // Iterate through all list entries, starting at index 0.
  template <class Visitor>
  void Iterate(Visitor* visitor);

  // Sort all list entries (using QuickSort)
  template <typename CompareFunction>
  void Sort(CompareFunction cmp);
  template <typename CompareFunction>
  void StableSort(CompareFunction cmp, size_t start, size_t length);

292 293
  void operator delete(void* pointer) { UNREACHABLE(); }
  void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
294 295 296 297 298 299

 private:
  T* data_;
  int capacity_;
  int length_;

300
  V8_INLINE T* NewData(int n, ZoneAllocationPolicy allocator) {
301 302
    return static_cast<T*>(allocator.New(n * sizeof(T)));
  }
303
  V8_INLINE void DeleteData(T* data) { ZoneAllocationPolicy::Delete(data); }
304 305 306 307 308 309 310 311 312 313 314 315 316

  // Increase the capacity of a full list, and add an element.
  // List must be full already.
  void ResizeAdd(const T& element, ZoneAllocationPolicy allocator);

  // Inlined implementation of ResizeAdd, shared by inlined and
  // non-inlined versions of ResizeAdd.
  void ResizeAddInternal(const T& element, ZoneAllocationPolicy allocator);

  // Resize the list.
  void Resize(int new_capacity, ZoneAllocationPolicy allocator);

  DISALLOW_COPY_AND_ASSIGN(ZoneList);
317 318
};

319 320 321 322 323
// ZonePtrList is a ZoneList of pointers to ZoneObjects allocated in the same
// zone as the list object.
template <typename T>
using ZonePtrList = ZoneList<T*>;

324 325 326
template <typename T>
class ScopedPtrList final {
 public:
327 328
  explicit ScopedPtrList(std::vector<void*>* buffer)
      : buffer_(*buffer), start_(buffer->size()), end_(buffer->size()) {}
329

330 331 332
  ~ScopedPtrList() { Rewind(); }

  void Rewind() {
333 334
    DCHECK_EQ(buffer_.size(), end_);
    buffer_.resize(start_);
335
    end_ = start_;
336 337
  }

338 339 340 341 342 343
  int length() const { return static_cast<int>(end_ - start_); }
  T* at(int i) const {
    size_t index = start_ + i;
    DCHECK_LT(index, buffer_.size());
    return reinterpret_cast<T*>(buffer_[index]);
  }
344

345 346 347 348 349 350 351
  void CopyTo(ZonePtrList<T>* target, Zone* zone) const {
    DCHECK_LE(end_, buffer_.size());
    // Make sure we don't reference absent elements below.
    if (length() == 0) return;
    target->Initialize(length(), zone);
    T** data = reinterpret_cast<T**>(&buffer_[start_]);
    target->AddAll(Vector<T*>(data, length()), zone);
352 353 354
  }

  void Add(T* value) {
355 356
    DCHECK_EQ(buffer_.size(), end_);
    buffer_.push_back(value);
357 358 359 360
    ++end_;
  }

  void AddAll(const ZonePtrList<T>& list) {
361 362 363 364 365
    DCHECK_EQ(buffer_.size(), end_);
    buffer_.reserve(buffer_.size() + list.length());
    for (int i = 0; i < list.length(); i++) {
      buffer_.push_back(list.at(i));
    }
366 367 368 369
    end_ += list.length();
  }

 private:
370 371 372
  std::vector<void*>& buffer_;
  size_t start_;
  size_t end_;
373 374
};

375 376
// ZoneThreadedList is a special variant of the ThreadedList that can be put
// into a Zone.
377 378
template <typename T, typename TLTraits = base::ThreadedListTraits<T>>
using ZoneThreadedList = base::ThreadedListBase<T, ZoneObject, TLTraits>;
379

380
// A zone splay tree.  The config type parameter encapsulates the
381 382
// different configurations of a concrete splay tree (see splay-tree.h).
// The tree itself and all its elements are allocated in the Zone.
383
template <typename Config>
384
class ZoneSplayTree final : public SplayTree<Config, ZoneAllocationPolicy> {
385
 public:
386 387
  explicit ZoneSplayTree(Zone* zone)
      : SplayTree<Config, ZoneAllocationPolicy>(ZoneAllocationPolicy(zone)) {}
388 389 390 391 392 393
  ~ZoneSplayTree() {
    // Reset the root to avoid unneeded iteration over all tree nodes
    // in the destructor.  For a zone-allocated tree, nodes will be
    // freed by the Zone.
    SplayTree<Config, ZoneAllocationPolicy>::ResetRoot();
  }
394

395
  void* operator new(size_t size, Zone* zone) { return zone->New(size); }
396 397 398

  void operator delete(void* pointer) { UNREACHABLE(); }
  void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
399 400
};

401
typedef base::PointerTemplateHashMapImpl<ZoneAllocationPolicy> ZoneHashMap;
402

403 404 405
typedef base::CustomMatcherTemplateHashMapImpl<ZoneAllocationPolicy>
    CustomMatcherZoneHashMap;

406 407
}  // namespace internal
}  // namespace v8
408

409 410 411 412 413 414 415 416 417 418 419 420 421
// The accidential pattern
//    new (zone) SomeObject()
// where SomeObject does not inherit from ZoneObject leads to nasty crashes.
// This triggers a compile-time error instead.
template <class T, typename = typename std::enable_if<std::is_convertible<
                       T, const v8::internal::Zone*>::value>::type>
void* operator new(size_t size, T zone) {
  static_assert(false && sizeof(T),
                "Placement new with a zone is only permitted for classes "
                "inheriting from ZoneObject");
  UNREACHABLE();
}

422
#endif  // V8_ZONE_ZONE_H_