src/Spaces.h
author David Anderson <dvander@alliedmods.net>
Wed Dec 26 17:18:15 2012 -0800 (2012-12-26)
changeset 242 b7d14a8b918e
parent 91 5f4402180204
child 256 3c184218462d
permissions -rw-r--r--
Fix typo.
     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         size_t 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 };
   277 
   278 class FreedObject
   279 {
   280     Map *map_;
   281     size_t bytes_;
   282     FreedObject *next_;
   283 
   284   public:
   285     FreedObject(Map *map, size_t bytes)
   286       : map_(map),
   287         bytes_(bytes)
   288     {
   289         assert(bytes_ >= sizeof(Map *) + sizeof(size_t));
   290         if (bytes_ >= sizeof(FreedObject))
   291             next_ = NULL;
   292     }
   293 
   294     void setNext(FreedObject *obj) {
   295         assert(bytes_ >= sizeof(FreedObject));
   296         next_ = obj;
   297     }
   298     FreedObject *next() {
   299         assert(bytes_ >= sizeof(FreedObject));
   300         return next_;
   301     }
   302     size_t size() {
   303         return bytes_;
   304     }
   305 };
   306 
   307 // Free-region list used in old spaces. Based on V8's. Each object pool
   308 // is used to allocate objects of the previous pool's size. For example,
   309 // the medium object pool is used to allocate small objects.
   310 class FreeList
   311 {
   312   private:
   313     static const size_t kMinObjectSize = 32;
   314     static const size_t kMaxSmallObject = 255;
   315     static const size_t kMaxMediumObject = (2 * kKB) - 1;
   316     static const size_t kMaxLargeObject = (16 * kKB) - 1;
   317 
   318   public:
   319     FreeList();
   320 
   321   public:
   322     void add(FreedObject *obj);
   323     FreedObject *pick(size_t size);
   324     void reset();
   325 
   326   private:
   327     FreedObject *small_objects_;
   328     FreedObject *medium_objects_;
   329     FreedObject *large_objects_;
   330     FreedObject *huge_objects_;
   331 };
   332 
   333 // The heap stack is designed for structures which are known to not escape the
   334 // program stack, and can thus be quickly allocated in a LIFO manner.
   335 // 
   336 class HeapStack
   337 {
   338     OS::AlignableMapping map_;
   339     HeapChunk *chunk_;
   340     Address cursor_;
   341     uintptr_t mask_;
   342 
   343   private:
   344     static const size_t kStackSize = 1024 * 1024;
   345 
   346   public:
   347     HeapStack();
   348     ~HeapStack();
   349     bool initialize();
   350 
   351   public:
   352     Address position() const {
   353         return cursor_;
   354     }
   355     inline Address allocate(size_t nbytes);
   356     inline void unwind(Address to);
   357 
   358     inline bool contains(Object *obj) {
   359         return (reinterpret_cast<uintptr_t>(obj) & mask_) == reinterpret_cast<uintptr_t>(chunk_);
   360     }
   361 
   362     template <typename Evaluator>
   363     ChunkObjectIteratorBase<Evaluator> begin(const Evaluator &evaluator) {
   364         return ChunkObjectIteratorBase<Evaluator>(chunk_, cursor_, evaluator);
   365     }
   366 };
   367 
   368 class OldSpace
   369 {
   370     Zone *zone_;
   371     HeapChunk *first_;
   372     HeapChunk *last_;
   373     Address cursor_;
   374     FreeList freelist_;
   375 
   376   public:
   377     OldSpace(Zone *zone);
   378     ~OldSpace();
   379 
   380     bool initialize();
   381 
   382   public:
   383     inline Address allocate(size_t nbytes);
   384     Address allocateSlow(size_t nbytes);
   385     void markRegionFree(Address start, size_t size);
   386     void freeUnusedPages();
   387 
   388     template <typename Evaluator>
   389     ChunkObjectIteratorBase<Evaluator> begin(const Evaluator &evaluator) {
   390         return ChunkObjectIteratorBase<Evaluator>(first_, cursor_, evaluator);
   391     }
   392 
   393     HeapChunk *first() {
   394         return first_;
   395     }
   396     void resetFreeList();
   397 };
   398 
   399 class LargeObjectSpace
   400 {
   401     HeapChunk *first_;
   402     HeapChunk *last_;
   403 
   404   public:
   405     LargeObjectSpace();
   406     ~LargeObjectSpace();
   407 
   408     bool initialize();
   409 
   410   public:
   411     Address allocate(size_t nbytes);
   412 
   413     HeapChunk *first() {
   414         return first_;
   415     }
   416     void sweep();
   417 };
   418 
   419 }
   420 
   421 #endif // _include_jitcraft_spaces_h_