blob: aa76091621bb835eba4c634ad28af277a0c5e1f4 [file] [log] [blame]
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_ZONE_ZONE_H_
#define V8_ZONE_ZONE_H_
#include <limits>
#include "src/base/logging.h"
#include "src/common/globals.h"
#include "src/utils/utils.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/type-stats.h"
#include "src/zone/zone-segment.h"
#include "src/zone/zone-type-traits.h"
#ifndef ZONE_NAME
#define ZONE_NAME __func__
#endif
namespace v8 {
namespace internal {
// 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.
//
// Note: There is no need to initialize the Zone; the first time an
// allocation is attempted, a segment of memory will be requested
// through the allocator.
//
// Note: The implementation is inherently not thread safe. Do not use
// from multi-threaded code.
class V8_EXPORT_PRIVATE Zone final {
public:
Zone(AccountingAllocator* allocator, const char* name,
bool support_compression = false);
~Zone();
// Returns true if the zone supports zone pointer compression.
bool supports_compression() const {
return COMPRESS_ZONES_BOOL && supports_compression_;
}
// 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()).
//
// When V8_ENABLE_PRECISE_ZONE_STATS is defined, the allocated bytes are
// associated with the provided TypeTag type.
template <typename TypeTag>
void* Allocate(size_t size) {
#ifdef V8_USE_ADDRESS_SANITIZER
return AsanNew(size);
#else
size = RoundUp(size, kAlignmentInBytes);
#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
Address result = position_;
if (V8_UNLIKELY(size > limit_ - position_)) {
result = NewExpand(size);
} else {
position_ += size;
}
return reinterpret_cast<void*>(result);
#endif // V8_USE_ADDRESS_SANITIZER
}
// 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.
template <typename TypeTag = void>
void Delete(void* pointer, size_t size) {
DCHECK_NOT_NULL(pointer);
DCHECK_NE(size, 0);
size = RoundUp(size, kAlignmentInBytes);
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
if (V8_UNLIKELY(TracingFlags::is_zone_stats_enabled())) {
type_stats_.AddDeallocated<TypeTag>(size);
}
freed_size_for_tracing_ += size;
#endif
#ifdef DEBUG
static const unsigned char kZapDeadByte = 0xcd;
memset(pointer, kZapDeadByte, size);
#endif
}
// Allocates memory for T instance and constructs object by calling respective
// Args... constructor.
//
// When V8_ENABLE_PRECISE_ZONE_STATS is defined, the allocated bytes are
// associated with the T type.
template <typename T, typename... Args>
T* New(Args&&... args) {
void* memory = Allocate<T>(sizeof(T));
return new (memory) T(std::forward<Args>(args)...);
}
// Allocates uninitialized memory for 'length' number of T instances.
//
// 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.
template <typename T, typename TypeTag = T[]>
T* NewArray(size_t length) {
DCHECK_IMPLIES(is_compressed_pointer<T>::value, supports_compression());
DCHECK_LT(length, std::numeric_limits<size_t>::max() / sizeof(T));
return static_cast<T*>(Allocate<TypeTag>(length * sizeof(T)));
}
// 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.
template <typename T, typename TypeTag = T[]>
void DeleteArray(T* pointer, size_t length) {
Delete<TypeTag>(pointer, length * sizeof(T));
}
// Seals the zone to prevent any further allocation.
void Seal() { sealed_ = true; }
// Allows the zone to be safely reused. Releases the memory and fires zone
// destruction and creation events for the accounting allocator.
void ReleaseMemory();
// Returns true if more memory has been allocated in zones than
// the limit allows.
bool excess_allocation() const {
return segment_bytes_allocated_ > kExcessLimit;
}
size_t segment_bytes_allocated() const { return segment_bytes_allocated_; }
const char* name() const { return name_; }
// Returns precise value of used zone memory, allowed to be called only
// from thread owning the zone.
size_t allocation_size() const {
size_t extra = segment_head_ ? position_ - segment_head_->start() : 0;
return allocation_size_ + extra;
}
// When V8_ENABLE_PRECISE_ZONE_STATS is not defined, returns used zone memory
// 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
}
// 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
}
AccountingAllocator* allocator() const { return allocator_; }
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
const TypeStats& type_stats() const { return type_stats_; }
#endif
private:
void* AsanNew(size_t size);
// Deletes all objects and free all memory allocated in the Zone.
void DeleteAll();
// All pointers returned from New() are 8-byte aligned.
static const size_t kAlignmentInBytes = 8;
// Never allocate segments smaller than this size in bytes.
static const size_t kMinimumSegmentSize = 8 * KB;
// Never allocate segments larger than this size in bytes.
static const size_t kMaximumSegmentSize = 32 * KB;
// Report zone excess when allocation exceeds this limit.
static const size_t kExcessLimit = 256 * MB;
// The number of bytes allocated in this zone so far.
size_t allocation_size_ = 0;
// The number of bytes allocated in segments. Note that this number
// includes memory allocated from the OS but not yet allocated from
// the zone.
size_t segment_bytes_allocated_ = 0;
// 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.
Address NewExpand(size_t size);
// 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.
Address position_ = 0;
Address limit_ = 0;
AccountingAllocator* allocator_;
Segment* segment_head_ = nullptr;
const char* name_;
const bool supports_compression_;
bool sealed_ = false;
#ifdef V8_ENABLE_PRECISE_ZONE_STATS
TypeStats type_stats_;
size_t allocation_size_for_tracing_ = 0;
// The number of bytes freed in this zone so far.
size_t freed_size_for_tracing_ = 0;
#endif
};
// 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:
// 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.
// Allow non-allocating placement new.
void* operator new(size_t size, void* ptr) { // See explanation above.
return ptr;
}
// Ideally, the delete operator should be private instead of
// public, but unfortunately the compiler sometimes synthesizes
// (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.
// Note, that descructors will not be called.
void operator delete(void*, size_t) { UNREACHABLE(); }
void operator delete(void* pointer, Zone* zone) = delete;
};
// The ZoneAllocationPolicy is used to specialize generic data
// structures to allocate themselves and their elements in the Zone.
class ZoneAllocationPolicy {
public:
// Creates unusable allocation policy.
ZoneAllocationPolicy() : zone_(nullptr) {}
explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) {}
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);
}
Zone* zone() const { return zone_; }
private:
Zone* zone_;
};
} // namespace internal
} // namespace v8
// 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.
#endif // V8_ZONE_ZONE_H_