zone.h 11.5 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
#include <limits>
9 10
#include <memory>
#include <type_traits>
11

12
#include "src/base/logging.h"
13
#include "src/base/vector.h"
14
#include "src/common/globals.h"
15
#include "src/utils/utils.h"
16
#include "src/zone/accounting-allocator.h"
17
#include "src/zone/type-stats.h"
18
#include "src/zone/zone-segment.h"
19
#include "src/zone/zone-type-traits.h"
20

21
#ifndef ZONE_NAME
22
#define ZONE_NAME __func__
23 24
#endif

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

// 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.
33
//
34 35
// Note: There is no need to initialize the Zone; the first time an
// allocation is attempted, a segment of memory will be requested
36
// through the allocator.
37
//
38 39
// Note: The implementation is inherently not thread safe. Do not use
// from multi-threaded code.
40

41
class V8_EXPORT_PRIVATE Zone final {
42
 public:
43 44
  Zone(AccountingAllocator* allocator, const char* name,
       bool support_compression = false);
45
  ~Zone();
46

47 48 49 50 51
  // Returns true if the zone supports zone pointer compression.
  bool supports_compression() const {
    return COMPRESS_ZONES_BOOL && supports_compression_;
  }

52 53 54
  // Allocate 'size' bytes of uninitialized memory in the Zone; expands the Zone
  // by allocating new segments of memory on demand using AccountingAllocator
  // (see AccountingAllocator::AllocateSegment()).
55 56 57
  //
  // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the allocated bytes are
  // associated with the provided TypeTag type.
58 59
  template <typename TypeTag>
  void* Allocate(size_t size) {
60 61 62 63
#ifdef V8_USE_ADDRESS_SANITIZER
    return AsanNew(size);
#else
    size = RoundUp(size, kAlignmentInBytes);
64 65 66 67 68 69
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
    if (V8_UNLIKELY(TracingFlags::is_zone_stats_enabled())) {
      type_stats_.AddAllocated<TypeTag>(size);
    }
    allocation_size_for_tracing_ += size;
#endif
70 71 72 73 74 75 76
    Address result = position_;
    if (V8_UNLIKELY(size > limit_ - position_)) {
      result = NewExpand(size);
    } else {
      position_ += size;
    }
    return reinterpret_cast<void*>(result);
77
#endif  // V8_USE_ADDRESS_SANITIZER
78
  }
79

80 81 82 83 84
  // Return 'size' bytes of memory back to Zone. These bytes can be reused
  // for following allocations.
  //
  // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the deallocated bytes are
  // associated with the provided TypeTag type.
85 86 87 88 89
  template <typename TypeTag = void>
  void Delete(void* pointer, size_t size) {
    DCHECK_NOT_NULL(pointer);
    DCHECK_NE(size, 0);
    size = RoundUp(size, kAlignmentInBytes);
90 91 92 93
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
    if (V8_UNLIKELY(TracingFlags::is_zone_stats_enabled())) {
      type_stats_.AddDeallocated<TypeTag>(size);
    }
94
    freed_size_for_tracing_ += size;
95 96 97
#endif

#ifdef DEBUG
98 99 100 101 102
    static const unsigned char kZapDeadByte = 0xcd;
    memset(pointer, kZapDeadByte, size);
#endif
  }

103 104
  // Allocates memory for T instance and constructs object by calling respective
  // Args... constructor.
105 106 107
  //
  // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the allocated bytes are
  // associated with the T type.
108 109
  template <typename T, typename... Args>
  T* New(Args&&... args) {
110
    void* memory = Allocate<T>(sizeof(T));
111 112 113 114
    return new (memory) T(std::forward<Args>(args)...);
  }

  // Allocates uninitialized memory for 'length' number of T instances.
115 116 117 118 119
  //
  // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the allocated bytes are
  // associated with the provided TypeTag type. It might be useful to tag
  // buffer allocations with meaningful names to make buffer allocation sites
  // distinguishable between each other.
120
  template <typename T, typename TypeTag = T[]>
121
  T* NewArray(size_t length) {
122
    DCHECK_IMPLIES(is_compressed_pointer<T>::value, supports_compression());
123
    DCHECK_LT(length, std::numeric_limits<size_t>::max() / sizeof(T));
124
    return static_cast<T*>(Allocate<TypeTag>(length * sizeof(T)));
125
  }
126

127 128 129 130 131 132
  template <typename T, typename TypeTag = T[]>
  base::Vector<T> NewVector(size_t length) {
    T* new_array = NewArray<T, TypeTag>(length);
    return {new_array, length};
  }

133 134 135 136 137 138 139 140 141 142 143 144 145 146
  template <typename T, typename TypeTag = T[]>
  base::Vector<T> NewVector(size_t length, T value) {
    T* new_array = NewArray<T, TypeTag>(length);
    std::uninitialized_fill_n(new_array, length, value);
    return {new_array, length};
  }

  template <typename T, typename TypeTag = std::remove_const_t<T>[]>
  base::Vector<std::remove_const_t<T>> CloneVector(base::Vector<T> v) {
    auto* new_array = NewArray<std::remove_const_t<T>, TypeTag>(v.size());
    std::uninitialized_copy(v.begin(), v.end(), new_array);
    return {new_array, v.size()};
  }

147 148 149 150 151
  // Return array of 'length' elements back to Zone. These bytes can be reused
  // for following allocations.
  //
  // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the deallocated bytes are
  // associated with the provided TypeTag type.
152
  template <typename T, typename TypeTag = T[]>
153
  void DeleteArray(T* pointer, size_t length) {
154
    Delete<TypeTag>(pointer, length * sizeof(T));
155 156
  }

157 158 159
  // Seals the zone to prevent any further allocation.
  void Seal() { sealed_ = true; }

160 161 162 163
  // Allows the zone to be safely reused. Releases the memory except for the
  // last page, and fires zone destruction and creation events for the
  // accounting allocator.
  void Reset();
164

165 166
  size_t segment_bytes_allocated() const { return segment_bytes_allocated_; }

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

169 170
  // Returns precise value of used zone memory, allowed to be called only
  // from thread owning the zone.
171 172 173 174
  size_t allocation_size() const {
    size_t extra = segment_head_ ? position_ - segment_head_->start() : 0;
    return allocation_size_ + extra;
  }
175

176
  // When V8_ENABLE_PRECISE_ZONE_STATS is not defined, returns used zone memory
177 178 179 180 181 182 183 184 185
  // not including the head segment.
  // Can be called from threads not owning the zone.
  size_t allocation_size_for_tracing() const {
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
    return allocation_size_for_tracing_;
#else
    return allocation_size_;
#endif
  }
186

187 188 189 190 191 192 193 194 195 196 197
  // Returns number of bytes freed in this zone via Delete<T>()/DeleteArray<T>()
  // calls. Returns non-zero values only when V8_ENABLE_PRECISE_ZONE_STATS is
  // defined.
  size_t freed_size_for_tracing() const {
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
    return freed_size_for_tracing_;
#else
    return 0;
#endif
  }

198
  AccountingAllocator* allocator() const { return allocator_; }
199

200 201 202 203
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
  const TypeStats& type_stats() const { return type_stats_; }
#endif

204
 private:
205 206
  void* AsanNew(size_t size);

207 208 209
  // Deletes all objects and free all memory allocated in the Zone.
  void DeleteAll();

210 211 212 213
  // Releases the current segment without performing any local bookkeeping
  // (e.g. tracking allocated bytes, maintaining linked lists, etc).
  void ReleaseSegment(Segment* segment);

214 215
  // All pointers returned from New() are 8-byte aligned.
  static const size_t kAlignmentInBytes = 8;
216 217

  // Never allocate segments smaller than this size in bytes.
218 219 220
  static const size_t kMinimumSegmentSize = 8 * KB;

  // Never allocate segments larger than this size in bytes.
221
  static const size_t kMaximumSegmentSize = 32 * KB;
222

223
  // The number of bytes allocated in this zone so far.
224
  std::atomic<size_t> allocation_size_ = {0};
225

226 227 228
  // The number of bytes allocated in segments.  Note that this number
  // includes memory allocated from the OS but not yet allocated from
  // the zone.
229
  std::atomic<size_t> segment_bytes_allocated_ = {0};
230 231 232 233 234

  // 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.
235
  Address NewExpand(size_t size);
236

237 238 239
  // 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.
240 241
  Address position_ = 0;
  Address limit_ = 0;
242

243
  AccountingAllocator* allocator_;
244

245
  Segment* segment_head_ = nullptr;
heimbuef's avatar
heimbuef committed
246
  const char* name_;
247
  const bool supports_compression_;
248 249 250 251
  bool sealed_ = false;

#ifdef V8_ENABLE_PRECISE_ZONE_STATS
  TypeStats type_stats_;
252
  std::atomic<size_t> allocation_size_for_tracing_ = {0};
253 254

  // The number of bytes freed in this zone so far.
255
  stdd::atomic<size_t> freed_size_for_tracing_ = {0};
256
#endif
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280

  friend class ZoneScope;
};

// Similar to the HandleScope, the ZoneScope defines a region of validity for
// zone memory. All memory allocated in the given Zone during the scope's
// lifetime is freed when the scope is destructed, i.e. the Zone is reset to
// the state it was in when the scope was created.
class ZoneScope final {
 public:
  explicit ZoneScope(Zone* zone);
  ~ZoneScope();

 private:
  Zone* const zone_;
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
  const size_t allocation_size_for_tracing_;
  const size_t freed_size_for_tracing_;
#endif
  const size_t allocation_size_;
  const size_t segment_bytes_allocated_;
  const Address position_;
  const Address limit_;
  Segment* const segment_head_;
281 282 283 284 285 286
};

// 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:
287 288 289 290 291 292
  // The accidential old-style pattern
  //    new (zone) SomeObject(...)
  // now produces compilation error. The proper way of allocating objects in
  // Zones looks like this:
  //    zone->New<SomeObject>(...)
  void* operator new(size_t, Zone*) = delete;  // See explanation above.
293
  // Allow non-allocating placement new.
294 295 296
  void* operator new(size_t size, void* ptr) {  // See explanation above.
    return ptr;
  }
297 298

  // Ideally, the delete operator should be private instead of
299
  // public, but unfortunately the compiler sometimes synthesizes
300 301 302 303 304 305
  // (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.
306
  // Note, that descructors will not be called.
307
  void operator delete(void*, size_t) { UNREACHABLE(); }
308
  void operator delete(void* pointer, Zone* zone) = delete;
309 310
};

311 312
// The ZoneAllocationPolicy is used to specialize generic data
// structures to allocate themselves and their elements in the Zone.
313
class ZoneAllocationPolicy {
314
 public:
315 316
  // Creates unusable allocation policy.
  ZoneAllocationPolicy() : zone_(nullptr) {}
317
  explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) {}
318 319 320 321 322 323 324 325 326 327

  template <typename T, typename TypeTag = T[]>
  V8_INLINE T* NewArray(size_t length) {
    return zone()->NewArray<T, TypeTag>(length);
  }
  template <typename T, typename TypeTag = T[]>
  V8_INLINE void DeleteArray(T* p, size_t length) {
    zone()->DeleteArray<T, TypeTag>(p, length);
  }

328
  Zone* zone() const { return zone_; }
329

330 331
 private:
  Zone* zone_;
332 333
};

334 335
}  // namespace internal
}  // namespace v8
336

337 338 339 340 341 342 343
// The accidential old-style pattern
//    new (zone) SomeObject(...)
// now produces compilation error. The proper way of allocating objects in
// Zones looks like this:
//    zone->New<SomeObject>(...)
void* operator new(size_t, v8::internal::Zone*) = delete;   // See explanation.
void operator delete(void*, v8::internal::Zone*) = delete;  // See explanation.
344

345
#endif  // V8_ZONE_ZONE_H_