Home | History | Annotate | Download | only in src
      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_H_
      6 #define V8_ZONE_H_
      7 
      8 #include <limits>
      9 
     10 #include "src/base/accounting-allocator.h"
     11 #include "src/base/hashmap.h"
     12 #include "src/base/logging.h"
     13 #include "src/globals.h"
     14 #include "src/list.h"
     15 #include "src/splay-tree.h"
     16 
     17 namespace v8 {
     18 namespace internal {
     19 
     20 // Forward declarations.
     21 class Segment;
     22 
     23 
     24 // The Zone supports very fast allocation of small chunks of
     25 // memory. The chunks cannot be deallocated individually, but instead
     26 // the Zone supports deallocating all chunks in one fast
     27 // operation. The Zone is used to hold temporary data structures like
     28 // the abstract syntax tree, which is deallocated after compilation.
     29 //
     30 // Note: There is no need to initialize the Zone; the first time an
     31 // allocation is attempted, a segment of memory will be requested
     32 // through a call to malloc().
     33 //
     34 // Note: The implementation is inherently not thread safe. Do not use
     35 // from multi-threaded code.
     36 class Zone final {
     37  public:
     38   explicit Zone(base::AccountingAllocator* allocator);
     39   ~Zone();
     40 
     41   // Allocate 'size' bytes of memory in the Zone; expands the Zone by
     42   // allocating new segments of memory on demand using malloc().
     43   void* New(size_t size);
     44 
     45   template <typename T>
     46   T* NewArray(size_t length) {
     47     DCHECK_LT(length, std::numeric_limits<size_t>::max() / sizeof(T));
     48     return static_cast<T*>(New(length * sizeof(T)));
     49   }
     50 
     51   // Deletes all objects and free all memory allocated in the Zone. Keeps one
     52   // small (size <= kMaximumKeptSegmentSize) segment around if it finds one.
     53   void DeleteAll();
     54 
     55   // Deletes the last small segment kept around by DeleteAll(). You
     56   // may no longer allocate in the Zone after a call to this method.
     57   void DeleteKeptSegment();
     58 
     59   // Returns true if more memory has been allocated in zones than
     60   // the limit allows.
     61   bool excess_allocation() const {
     62     return segment_bytes_allocated_ > kExcessLimit;
     63   }
     64 
     65   size_t allocation_size() const { return allocation_size_; }
     66 
     67   base::AccountingAllocator* allocator() const { return allocator_; }
     68 
     69  private:
     70   // All pointers returned from New() have this alignment.  In addition, if the
     71   // object being allocated has a size that is divisible by 8 then its alignment
     72   // will be 8. ASan requires 8-byte alignment.
     73 #ifdef V8_USE_ADDRESS_SANITIZER
     74   static const size_t kAlignment = 8;
     75   STATIC_ASSERT(kPointerSize <= 8);
     76 #else
     77   static const size_t kAlignment = kPointerSize;
     78 #endif
     79 
     80   // Never allocate segments smaller than this size in bytes.
     81   static const size_t kMinimumSegmentSize = 8 * KB;
     82 
     83   // Never allocate segments larger than this size in bytes.
     84   static const size_t kMaximumSegmentSize = 1 * MB;
     85 
     86   // Never keep segments larger than this size in bytes around.
     87   static const size_t kMaximumKeptSegmentSize = 64 * KB;
     88 
     89   // Report zone excess when allocation exceeds this limit.
     90   static const size_t kExcessLimit = 256 * MB;
     91 
     92   // The number of bytes allocated in this zone so far.
     93   size_t allocation_size_;
     94 
     95   // The number of bytes allocated in segments.  Note that this number
     96   // includes memory allocated from the OS but not yet allocated from
     97   // the zone.
     98   size_t segment_bytes_allocated_;
     99 
    100   // Expand the Zone to hold at least 'size' more bytes and allocate
    101   // the bytes. Returns the address of the newly allocated chunk of
    102   // memory in the Zone. Should only be called if there isn't enough
    103   // room in the Zone already.
    104   Address NewExpand(size_t size);
    105 
    106   // Creates a new segment, sets it size, and pushes it to the front
    107   // of the segment chain. Returns the new segment.
    108   inline Segment* NewSegment(size_t size);
    109 
    110   // Deletes the given segment. Does not touch the segment chain.
    111   inline void DeleteSegment(Segment* segment, size_t size);
    112 
    113   // The free region in the current (front) segment is represented as
    114   // the half-open interval [position, limit). The 'position' variable
    115   // is guaranteed to be aligned as dictated by kAlignment.
    116   Address position_;
    117   Address limit_;
    118 
    119   base::AccountingAllocator* allocator_;
    120 
    121   Segment* segment_head_;
    122 };
    123 
    124 
    125 // ZoneObject is an abstraction that helps define classes of objects
    126 // allocated in the Zone. Use it as a base class; see ast.h.
    127 class ZoneObject {
    128  public:
    129   // Allocate a new ZoneObject of 'size' bytes in the Zone.
    130   void* operator new(size_t size, Zone* zone) { return zone->New(size); }
    131 
    132   // Ideally, the delete operator should be private instead of
    133   // public, but unfortunately the compiler sometimes synthesizes
    134   // (unused) destructors for classes derived from ZoneObject, which
    135   // require the operator to be visible. MSVC requires the delete
    136   // operator to be public.
    137 
    138   // ZoneObjects should never be deleted individually; use
    139   // Zone::DeleteAll() to delete all zone objects in one go.
    140   void operator delete(void*, size_t) { UNREACHABLE(); }
    141   void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
    142 };
    143 
    144 
    145 // The ZoneScope is used to automatically call DeleteAll() on a
    146 // Zone when the ZoneScope is destroyed (i.e. goes out of scope)
    147 class ZoneScope final {
    148  public:
    149   explicit ZoneScope(Zone* zone) : zone_(zone) { }
    150   ~ZoneScope() { zone_->DeleteAll(); }
    151 
    152   Zone* zone() const { return zone_; }
    153 
    154  private:
    155   Zone* zone_;
    156 };
    157 
    158 
    159 // The ZoneAllocationPolicy is used to specialize generic data
    160 // structures to allocate themselves and their elements in the Zone.
    161 class ZoneAllocationPolicy final {
    162  public:
    163   explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) { }
    164   void* New(size_t size) { return zone()->New(size); }
    165   static void Delete(void* pointer) {}
    166   Zone* zone() const { return zone_; }
    167 
    168  private:
    169   Zone* zone_;
    170 };
    171 
    172 
    173 // ZoneLists are growable lists with constant-time access to the
    174 // elements. The list itself and all its elements are allocated in the
    175 // Zone. ZoneLists cannot be deleted individually; you can delete all
    176 // objects in the Zone by calling Zone::DeleteAll().
    177 template <typename T>
    178 class ZoneList final : public List<T, ZoneAllocationPolicy> {
    179  public:
    180   // Construct a new ZoneList with the given capacity; the length is
    181   // always zero. The capacity must be non-negative.
    182   ZoneList(int capacity, Zone* zone)
    183       : List<T, ZoneAllocationPolicy>(capacity, ZoneAllocationPolicy(zone)) { }
    184 
    185   void* operator new(size_t size, Zone* zone) { return zone->New(size); }
    186 
    187   // Construct a new ZoneList by copying the elements of the given ZoneList.
    188   ZoneList(const ZoneList<T>& other, Zone* zone)
    189       : List<T, ZoneAllocationPolicy>(other.length(),
    190                                       ZoneAllocationPolicy(zone)) {
    191     AddAll(other, zone);
    192   }
    193 
    194   // We add some convenience wrappers so that we can pass in a Zone
    195   // instead of a (less convenient) ZoneAllocationPolicy.
    196   void Add(const T& element, Zone* zone) {
    197     List<T, ZoneAllocationPolicy>::Add(element, ZoneAllocationPolicy(zone));
    198   }
    199   void AddAll(const List<T, ZoneAllocationPolicy>& other, Zone* zone) {
    200     List<T, ZoneAllocationPolicy>::AddAll(other, ZoneAllocationPolicy(zone));
    201   }
    202   void AddAll(const Vector<T>& other, Zone* zone) {
    203     List<T, ZoneAllocationPolicy>::AddAll(other, ZoneAllocationPolicy(zone));
    204   }
    205   void InsertAt(int index, const T& element, Zone* zone) {
    206     List<T, ZoneAllocationPolicy>::InsertAt(index, element,
    207                                             ZoneAllocationPolicy(zone));
    208   }
    209   Vector<T> AddBlock(T value, int count, Zone* zone) {
    210     return List<T, ZoneAllocationPolicy>::AddBlock(value, count,
    211                                                    ZoneAllocationPolicy(zone));
    212   }
    213   void Allocate(int length, Zone* zone) {
    214     List<T, ZoneAllocationPolicy>::Allocate(length, ZoneAllocationPolicy(zone));
    215   }
    216   void Initialize(int capacity, Zone* zone) {
    217     List<T, ZoneAllocationPolicy>::Initialize(capacity,
    218                                               ZoneAllocationPolicy(zone));
    219   }
    220 
    221   void operator delete(void* pointer) { UNREACHABLE(); }
    222   void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
    223 };
    224 
    225 
    226 // A zone splay tree.  The config type parameter encapsulates the
    227 // different configurations of a concrete splay tree (see splay-tree.h).
    228 // The tree itself and all its elements are allocated in the Zone.
    229 template <typename Config>
    230 class ZoneSplayTree final : public SplayTree<Config, ZoneAllocationPolicy> {
    231  public:
    232   explicit ZoneSplayTree(Zone* zone)
    233       : SplayTree<Config, ZoneAllocationPolicy>(ZoneAllocationPolicy(zone)) {}
    234   ~ZoneSplayTree() {
    235     // Reset the root to avoid unneeded iteration over all tree nodes
    236     // in the destructor.  For a zone-allocated tree, nodes will be
    237     // freed by the Zone.
    238     SplayTree<Config, ZoneAllocationPolicy>::ResetRoot();
    239   }
    240 
    241   void* operator new(size_t size, Zone* zone) { return zone->New(size); }
    242 
    243   void operator delete(void* pointer) { UNREACHABLE(); }
    244   void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
    245 };
    246 
    247 typedef base::TemplateHashMapImpl<ZoneAllocationPolicy> ZoneHashMap;
    248 
    249 }  // namespace internal
    250 }  // namespace v8
    251 
    252 #endif  // V8_ZONE_H_
    253