zone.h 10 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>

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

18
#ifndef ZONE_NAME
19
#define ZONE_NAME __func__
20 21
#endif

22 23
namespace v8 {
namespace internal {
24 25 26 27 28 29

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

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

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

49 50 51
  // 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()).
52 53 54
  //
  // When V8_ENABLE_PRECISE_ZONE_STATS is defined, the allocated bytes are
  // associated with the provided TypeTag type.
55 56
  template <typename TypeTag>
  void* Allocate(size_t size) {
57 58 59 60
#ifdef V8_USE_ADDRESS_SANITIZER
    return AsanNew(size);
#else
    size = RoundUp(size, kAlignmentInBytes);
61 62 63 64 65 66
#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
67 68 69 70 71 72 73
    Address result = position_;
    if (V8_UNLIKELY(size > limit_ - position_)) {
      result = NewExpand(size);
    } else {
      position_ += size;
    }
    return reinterpret_cast<void*>(result);
74
#endif  // V8_USE_ADDRESS_SANITIZER
75
  }
76

77 78 79 80 81
  // 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.
82 83 84 85 86
  template <typename TypeTag = void>
  void Delete(void* pointer, size_t size) {
    DCHECK_NOT_NULL(pointer);
    DCHECK_NE(size, 0);
    size = RoundUp(size, kAlignmentInBytes);
87 88 89 90
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
    if (V8_UNLIKELY(TracingFlags::is_zone_stats_enabled())) {
      type_stats_.AddDeallocated<TypeTag>(size);
    }
91
    freed_size_for_tracing_ += size;
92 93 94
#endif

#ifdef DEBUG
95 96 97 98 99
    static const unsigned char kZapDeadByte = 0xcd;
    memset(pointer, kZapDeadByte, size);
#endif
  }

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

  // Allocates uninitialized memory for 'length' number of T instances.
112 113 114 115 116
  //
  // 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.
117
  template <typename T, typename TypeTag = T[]>
118
  T* NewArray(size_t length) {
119
    DCHECK_IMPLIES(is_compressed_pointer<T>::value, supports_compression());
120
    DCHECK_LT(length, std::numeric_limits<size_t>::max() / sizeof(T));
121
    return static_cast<T*>(Allocate<TypeTag>(length * sizeof(T)));
122
  }
123

124 125 126 127 128
  // 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.
129
  template <typename T, typename TypeTag = T[]>
130
  void DeleteArray(T* pointer, size_t length) {
131
    Delete<TypeTag>(pointer, length * sizeof(T));
132 133
  }

134 135 136
  // Seals the zone to prevent any further allocation.
  void Seal() { sealed_ = true; }

137 138 139
  // Allows the zone to be safely reused. Releases the memory and fires zone
  // destruction and creation events for the accounting allocator.
  void ReleaseMemory();
140

141 142
  // Returns true if more memory has been allocated in zones than
  // the limit allows.
143 144 145
  bool excess_allocation() const {
    return segment_bytes_allocated_ > kExcessLimit;
  }
146

147 148
  size_t segment_bytes_allocated() const { return segment_bytes_allocated_; }

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

151 152
  // Returns precise value of used zone memory, allowed to be called only
  // from thread owning the zone.
153 154 155 156
  size_t allocation_size() const {
    size_t extra = segment_head_ ? position_ - segment_head_->start() : 0;
    return allocation_size_ + extra;
  }
157

158
  // When V8_ENABLE_PRECISE_ZONE_STATS is not defined, returns used zone memory
159 160 161 162 163 164 165 166 167
  // 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
  }
168

169 170 171 172 173 174 175 176 177 178 179
  // 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
  }

180
  AccountingAllocator* allocator() const { return allocator_; }
181

182 183 184 185
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
  const TypeStats& type_stats() const { return type_stats_; }
#endif

186
 private:
187 188
  void* AsanNew(size_t size);

189 190 191
  // Deletes all objects and free all memory allocated in the Zone.
  void DeleteAll();

192 193
  // All pointers returned from New() are 8-byte aligned.
  static const size_t kAlignmentInBytes = 8;
194 195

  // Never allocate segments smaller than this size in bytes.
196 197 198
  static const size_t kMinimumSegmentSize = 8 * KB;

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

201
  // Report zone excess when allocation exceeds this limit.
202
  static const size_t kExcessLimit = 256 * MB;
203

204
  // The number of bytes allocated in this zone so far.
205
  size_t allocation_size_ = 0;
206

207 208 209
  // The number of bytes allocated in segments.  Note that this number
  // includes memory allocated from the OS but not yet allocated from
  // the zone.
210
  size_t segment_bytes_allocated_ = 0;
211 212 213 214 215

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

218 219 220
  // 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.
221 222
  Address position_ = 0;
  Address limit_ = 0;
223

224
  AccountingAllocator* allocator_;
225

226
  Segment* segment_head_ = nullptr;
heimbuef's avatar
heimbuef committed
227
  const char* name_;
228
  const bool supports_compression_;
229 230 231 232 233
  bool sealed_ = false;

#ifdef V8_ENABLE_PRECISE_ZONE_STATS
  TypeStats type_stats_;
  size_t allocation_size_for_tracing_ = 0;
234 235 236

  // The number of bytes freed in this zone so far.
  size_t freed_size_for_tracing_ = 0;
237
#endif
238 239 240 241 242 243
};

// 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:
244 245 246 247 248 249
  // 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.
250
  // Allow non-allocating placement new.
251 252 253
  void* operator new(size_t size, void* ptr) {  // See explanation above.
    return ptr;
  }
254 255

  // Ideally, the delete operator should be private instead of
256
  // public, but unfortunately the compiler sometimes synthesizes
257 258 259 260 261 262
  // (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.
263
  // Note, that descructors will not be called.
264
  void operator delete(void*, size_t) { UNREACHABLE(); }
265
  void operator delete(void* pointer, Zone* zone) = delete;
266 267
};

268 269
// The ZoneAllocationPolicy is used to specialize generic data
// structures to allocate themselves and their elements in the Zone.
270
class ZoneAllocationPolicy {
271
 public:
272 273
  // Creates unusable allocation policy.
  ZoneAllocationPolicy() : zone_(nullptr) {}
274
  explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) {}
275 276 277 278 279 280 281 282 283 284

  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);
  }

285
  Zone* zone() const { return zone_; }
286

287 288
 private:
  Zone* zone_;
289 290
};

291 292
}  // namespace internal
}  // namespace v8
293

294 295 296 297 298 299 300
// 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.
301

302
#endif  // V8_ZONE_ZONE_H_