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