jitcraft/Spaces.h
author David Anderson <dvander@alliedmods.net>
Mon Jul 23 01:19:44 2012 -0700 (2012-07-23)
changeset 85 06a0015b6a5c
parent 59 b7d61ddb3c8d
permissions -rw-r--r--
Added global variable support.
     1 /* vim: set ts=4 sw=4 tw=99 et:
     2  *
     3  * Copyright (C) 2012 David Anderson
     4  *
     5  * This file is part of JITCraft.
     6  *
     7  * JITCraft is free software: you can redistribute it and/or modify it under
     8  * the terms of the GNU General Public License as published by the Free
     9  * Software Foundation, either version 3 of the License, or (at your option)
    10  * any later version.
    11  * 
    12  * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
    13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License along with
    17  * JITCraft. If not, see http://www.gnu.org/licenses/.
    18  */
    19 #ifndef _include_jitcraft_spaces_h_
    20 #define _include_jitcraft_spaces_h_
    21 
    22 #include <limits.h>
    23 #include "Utility.h"
    24 #include "Platform.h"
    25 
    26 namespace ke {
    27 
    28 class Zone;
    29 class Object;
    30 class Map;
    31 
    32 static const size_t kObjectAlignment = sizeof(Object *);
    33 
    34 // Every object gets two successive mark bits. These bits represent various
    35 // states during garbage collection:
    36 //
    37 //  WHITE (00): The object is dead or unmarked.
    38 //  GREY  (11): The object is live but its children have not been marked.
    39 //  BLACK (10): The object and all its children have been marked.
    40 //
    41 // These colors assist in maintaining the "tri-color invariant", explained
    42 // in Heap.h. The bits are organized such that transitioning an object from
    43 // White to Black requires one bit change, and Black to Grey requires another.
    44 class MarkBit
    45 {
    46     uint32 *word_;
    47     uint32 bit_;
    48 
    49   public:
    50     MarkBit(uint32 *word, uint32 bit)
    51       : word_(word),
    52         bit_(bit)
    53     {
    54         assert(bit_ < sizeof(*word_) * 8);
    55     }
    56 
    57     bool isSet() const {
    58         return !!(*word_ & (1 << bit_));
    59     }
    60     void set() {
    61         *word_ |= (1 << bit_);
    62     }
    63     void unset() {
    64         *word_ &= ~(1 << bit_);
    65     }
    66     uint32 *word() {
    67         return word_;
    68     }
    69     MarkBit next() const {
    70         if (bit_ == (sizeof(*word_) * 8) - 1)
    71             return MarkBit(word_ + 1, 0);
    72         return MarkBit(word_, bit_ + 1);
    73     }
    74 };
    75 
    76 class HeapChunk
    77 {
    78     HeapChunk *next_;
    79 
    80     // Base address, from OS::AlignableMapping.
    81     Address base_;
    82 
    83     // End of the allocated region.
    84     Address end_;
    85 
    86     uintptr_t flags_;
    87 
    88   public:
    89     enum Flag {
    90         HAS_OVERFLOWED_OBJECT = (1 << 0),
    91         EMPTY = (1 << 1)
    92     };
    93 
    94   public:
    95     static const size_t kDefaultSize = 1 * kMB;
    96     static const size_t kAlignment = 1 * kMB;
    97 
    98     // Maximum size of an object that will fit in a page. We limit this
    99     // specifically to help JITs: the computation of a[i] has a special
   100     // instruction form on most CPUs, but the index might not be sign-
   101     // extended on 64-bit architectures. We limit the size such that:
   102     //
   103     //    index * sizeof(Object *)
   104     //
   105     // Is guaranteed to not overflow a signed 32-bit integer.
   106     static const size_t kMaxObjectSize = INT_MAX / sizeof(Object *);
   107 
   108     // Size of a HeapChunk header.
   109     static const size_t kHeaderSize =
   110         sizeof(HeapChunk *) +
   111         sizeof(Address) +
   112         sizeof(Address) +
   113         sizeof(Address);
   114 
   115     // All objects must be allocated in the first kAlignment bytes of the
   116     // chunk. We need two mark bits for every allocatable object, since there
   117     // are three possible states: White (00), Black (11), and Grey (10). These
   118     // bits are stored immediately after the HeapChunk header, and before the
   119     // first allocatable object.
   120     //
   121     // Although object alignment is only at word boundaries, we guarantee that
   122     // every object is at least two words. This means we don't need to reserve
   123     // two mark bits for every object, just one mark bit for every allocatable
   124     // address. That also means a pair of mark bits can cross a byte boundary.
   125     //
   126     // --- Math ---
   127     //
   128     // The number of mark bits needed is:
   129     //
   130     // MaxObjectsPerChunk = (kAlignment / kObjectAlignment)
   131     //                    -> 256KB (128KB on 64-bit)
   132     //
   133     // The number of bytes needed for these mark bits is:
   134     //
   135     // NumMarkBytes       = MaxObjectsPerChunk / 8
   136     //                    = 32KB (16KB on 64-bit)
   137     //
   138     // The number of uint32s needed to store these bytes is:
   139     //
   140     // NumMarkWords       = NumMarkBytes / sizeof(uint32)
   141     //                    = 8KB (4KB on 64-bit)
   142     //
   143     static const size_t kMaxObjectsPerChunk = kAlignment / kObjectAlignment;
   144     static const size_t kMarkBits = kMaxObjectsPerChunk;
   145     static const size_t kMarkBytes = kMarkBits / 8;
   146     static const size_t kMarkWords = kMarkBytes / sizeof(uint32);
   147     static const size_t kMarkBitsPerWord = sizeof(uint32) * 8;
   148 
   149     static const size_t kMarkBitsOffset = kHeaderSize;
   150     static const size_t kFirstObject = kMarkBitsOffset + kMarkBytes;
   151 
   152     // Threshold for allocating an object in the large object space.
   153     static const size_t kLargeObjectThreshold = kAlignment - kFirstObject;
   154 
   155     static inline void StaticAsserts() {
   156         STATIC_ASSERT(kHeaderSize == sizeof(HeapChunk));
   157         STATIC_ASSERT(IS_ALIGNED(kHeaderSize, kObjectAlignment));
   158         STATIC_ASSERT(IS_ALIGNED(kFirstObject, kObjectAlignment));
   159     }
   160 
   161   public:
   162     static HeapChunk *New(HeapChunk *prev, size_t size);
   163     static void Destroy(HeapChunk *chunk);
   164 
   165   public:
   166     HeapChunk *next() {
   167         return next_;
   168     }
   169     Address base() {
   170         return base_;
   171     }
   172     Address address() {
   173         return reinterpret_cast<Address>(this);
   174     }
   175     Address end() {
   176         return end_;
   177     }
   178     size_t size() {
   179         return end() - base();
   180     }
   181     Address first() {
   182         return reinterpret_cast<Address>(this) + kFirstObject;
   183     }
   184     HeapChunk **nextp() {
   185         return &next_;
   186     }
   187     uint32 *markBits() {
   188         return reinterpret_cast<uint32 *>(reinterpret_cast<Address>(this) + kMarkBitsOffset);
   189     }
   190     void resetMarkBits();
   191 
   192     // Marking functions.
   193     static inline MarkBit GetMarkBit(Object *obj) {
   194         uintptr_t address = reinterpret_cast<uintptr_t>(obj);
   195 
   196         HeapChunk *chunk = reinterpret_cast<HeapChunk *>(address & ~(kAlignment - 1));
   197         assert(Address(obj) >= chunk->first() && Address(obj) < chunk->end());
   198 
   199         size_t index = (address & (kAlignment - 1)) / kObjectAlignment;
   200         assert(index < kMaxObjectsPerChunk);
   201 
   202         uint32 *word = chunk->markBits() + (index / kMarkBitsPerWord);
   203         uint32 bit = index & (kMarkBitsPerWord - 1);
   204         return MarkBit(word, bit);
   205     }
   206     static inline Address FromMarkBit(HeapChunk *chunk, uint32 *word, uint32 bit) {
   207         // word = bits + (index / kMarkBitsPerWord)
   208         // word - bits = index / kMarkBitsPerWord
   209         // (word - bits) * kMarkBitsPerWord = index
   210         assert(word >= chunk->markBits() && word < chunk->markBits() + kMarkWords);
   211         uint32 index = ((word - chunk->markBits()) * kMarkBitsPerWord) + bit;
   212         return chunk->address() + (index * kObjectAlignment);
   213     }
   214     static inline bool IsWhite(const MarkBit &bit) {
   215         assert(bit.isSet() || !bit.next().isSet());
   216         return !bit.isSet();
   217     }
   218     static inline bool IsBlack(const MarkBit &bit) {
   219         return bit.isSet() && !bit.next().isSet();
   220     }
   221     static inline bool IsGrey(const MarkBit &bit) {
   222         return bit.isSet() && bit.next().isSet();
   223     }
   224     static inline bool IsBlackOrGrey(const MarkBit &bit) {
   225         return bit.isSet();
   226     }
   227     static inline void WhiteToBlack(MarkBit &bit) {
   228         assert(IsWhite(bit));
   229         bit.set();
   230     }
   231     static inline void BlackToGrey(MarkBit &bit) {
   232         assert(IsBlack(bit));
   233         bit.next().set();
   234     }
   235     static inline void GreyToBlack(MarkBit &bit) {
   236         assert(IsGrey(bit));
   237         bit.next().unset();
   238     }
   239     static inline void Clear(MarkBit &bit) {
   240         bit.unset();
   241         bit.next().unset();
   242     }
   243 
   244     bool hasOverflowedObject() {
   245         return !!(flags_ & HAS_OVERFLOWED_OBJECT);
   246     }
   247     void setHasOverflowedObject() {
   248         flags_ |= HAS_OVERFLOWED_OBJECT;
   249     }
   250     bool isEmpty() {
   251         return !!(flags_ & EMPTY);
   252     }
   253     void setEmpty() {
   254         flags_ |= EMPTY;
   255     }
   256 };
   257 
   258 template <typename SizeEvaluator>
   259 class ChunkObjectIteratorBase
   260 {
   261     HeapChunk *chunk_;
   262     Address top_;
   263     SizeEvaluator evaluator_;
   264     Address current_;
   265 
   266   public:
   267     ChunkObjectIteratorBase(HeapChunk *start, Address top, const SizeEvaluator &evaluator)
   268       : chunk_(start),
   269         top_(top),
   270         evaluator_(evaluator),
   271         current_(start ? start->first() : NULL)
   272     {
   273     }
   274 
   275     Object *next() {
   276         while (current_ && current_ != top_) {
   277             Object *obj = reinterpret_cast<Object *>(current_);
   278             size_t size = evaluator_(obj, obj->map());
   279             current_ += size;
   280             if (current_ == chunk_->end()) {
   281                 chunk_ = chunk_->next();
   282                 current_ = chunk_ ? chunk_->first() : NULL;
   283             }
   284             if (obj->is(MapKind_FreeRegion))
   285                 continue;
   286         }
   287         return NULL;
   288     }
   289 };
   290 
   291 class FreedObject
   292 {
   293     Map *map_;
   294     size_t bytes_;
   295     FreedObject *next_;
   296 
   297   public:
   298     FreedObject(Map *map, size_t bytes)
   299       : map_(map),
   300         bytes_(bytes)
   301     {
   302         assert(bytes_ >= sizeof(Map *) + sizeof(size_t));
   303         if (bytes_ >= sizeof(FreedObject))
   304             next_ = NULL;
   305     }
   306 
   307     void setNext(FreedObject *obj) {
   308         assert(bytes_ >= sizeof(FreedObject));
   309         next_ = obj;
   310     }
   311     FreedObject *next() {
   312         assert(bytes_ >= sizeof(FreedObject));
   313         return next_;
   314     }
   315     size_t size() {
   316         return bytes_;
   317     }
   318 };
   319 
   320 // Free-region list used in old spaces. Based on V8's. Each object pool
   321 // is used to allocate objects of the previous pool's size. For example,
   322 // the medium object pool is used to allocate small objects.
   323 class FreeList
   324 {
   325   private:
   326     static const size_t kMinObjectSize = 32;
   327     static const size_t kMaxSmallObject = 255;
   328     static const size_t kMaxMediumObject = (2 * kKB) - 1;
   329     static const size_t kMaxLargeObject = (16 * kKB) - 1;
   330 
   331   public:
   332     FreeList();
   333 
   334   public:
   335     void add(FreedObject *obj);
   336     FreedObject *pick(size_t size);
   337     void reset();
   338 
   339   private:
   340     FreedObject *small_objects_;
   341     FreedObject *medium_objects_;
   342     FreedObject *large_objects_;
   343     FreedObject *huge_objects_;
   344 };
   345 
   346 // The heap stack is designed for structures which are known to not escape the
   347 // program stack, and can thus be quickly allocated in a LIFO manner.
   348 // 
   349 class HeapStack
   350 {
   351     OS::AlignableMapping map_;
   352     HeapChunk *chunk_;
   353     Address cursor_;
   354     uintptr_t mask_;
   355 
   356   private:
   357     static const size_t kStackSize = 1024 * 1024;
   358 
   359   public:
   360     HeapStack();
   361     ~HeapStack();
   362     bool initialize();
   363 
   364   public:
   365     Address position() const {
   366         return cursor_;
   367     }
   368     inline Address allocate(size_t nbytes);
   369     inline void unwind(Address to);
   370 
   371     inline bool contains(Object *obj) {
   372         return (reinterpret_cast<uintptr_t>(obj) & mask_) == reinterpret_cast<uintptr_t>(chunk_);
   373     }
   374 
   375     template <typename Evaluator>
   376     ChunkObjectIteratorBase<Evaluator> begin(const Evaluator &evaluator) {
   377         return ChunkObjectIteratorBase<Evaluator>(chunk_, cursor_, evaluator);
   378     }
   379 };
   380 
   381 class OldSpace
   382 {
   383     Zone *zone_;
   384     HeapChunk *first_;
   385     HeapChunk *last_;
   386     Address cursor_;
   387     FreeList freelist_;
   388 
   389   public:
   390     OldSpace(Zone *zone);
   391     ~OldSpace();
   392 
   393     bool initialize();
   394 
   395   public:
   396     inline Address allocate(size_t nbytes);
   397     Address allocateSlow(size_t nbytes);
   398     void markRegionFree(Address start, size_t size);
   399     void freeUnusedPages();
   400 
   401     template <typename Evaluator>
   402     ChunkObjectIteratorBase<Evaluator> begin(const Evaluator &evaluator) {
   403         return ChunkObjectIteratorBase<Evaluator>(first_, cursor_, evaluator);
   404     }
   405 
   406     HeapChunk *first() {
   407         return first_;
   408     }
   409     void resetFreeList();
   410 };
   411 
   412 class LargeObjectSpace
   413 {
   414     HeapChunk *first_;
   415     HeapChunk *last_;
   416 
   417   public:
   418     LargeObjectSpace();
   419     ~LargeObjectSpace();
   420 
   421     bool initialize();
   422 
   423   public:
   424     Address allocate(size_t nbytes);
   425 
   426     HeapChunk *first() {
   427         return first_;
   428     }
   429     void sweep();
   430 };
   431 
   432 }
   433 
   434 #endif // _include_jitcraft_spaces_h_