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