src/Heap.cpp
author Michael Busby <prodigysim@gmail.com>
Mon Jun 10 01:50:18 2013 +0100 (2013-06-10)
changeset 264 70b8f318c379
parent 240 e3c851a14f16
permissions -rw-r--r--
Parse assignment RTL instead of LTR, fixes multiple assignment (bug 5420).
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@121
     5
 * This file is part of SourcePawn.
dvander@49
     6
 *
dvander@121
     7
 * SourcePawn 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@121
    12
 * SourcePawn 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@121
    17
 * SourcePawn. If not, see http://www.gnu.org/licenses/.
dvander@49
    18
 */
dvander@49
    19
#include "Heap.h"
dvander@49
    20
#include "Heap-inl.h"
dvander@49
    21
#include "Spaces-inl.h"
dvander@52
    22
#include "VirtualObjectVisitor.h"
dvander@52
    23
#include "ObjectVisitor.h"
dvander@52
    24
#include "Zone.h"
dvander@54
    25
#include "StringPool.h"
dvander@49
    26
dvander@59
    27
using namespace ke;
dvander@49
    28
dvander@52
    29
static const size_t kMarkStackSize = 256 * kKB;
dvander@52
    30
dvander@49
    31
Heap::Heap(Zone *zone)
dvander@52
    32
  : zone_(zone),
danderson@91
    33
    mapSpace_(zone),
dvander@53
    34
    oldSpace_(zone),
danderson@91
    35
    markStack_(NULL)
dvander@49
    36
{
dvander@49
    37
}
dvander@49
    38
dvander@49
    39
bool
dvander@49
    40
Heap::initialize()
dvander@49
    41
{
dvander@49
    42
    if (!lifoSpace_.initialize())
dvander@49
    43
        return false;
dvander@49
    44
    if (!oldSpace_.initialize())
dvander@49
    45
        return false;
dvander@49
    46
    if (!mapSpace_.initialize())
dvander@49
    47
        return false;
dvander@49
    48
    if (!largeSpace_.initialize())
dvander@49
    49
        return false;
dvander@49
    50
dvander@52
    51
    markStack_ = malloc(kMarkStackSize);
dvander@52
    52
    if (!markStack_)
dvander@52
    53
        return false;
dvander@52
    54
dvander@49
    55
    if ((metaMap_ = allocateMap(MapKind_Meta, sizeof(Map))) == NULL)
dvander@49
    56
        return false;
dvander@49
    57
    metaMap_->init(metaMap_);
dvander@49
    58
dvander@49
    59
    return true;
dvander@49
    60
}
dvander@49
    61
dvander@52
    62
Heap::~Heap()
dvander@52
    63
{
dvander@52
    64
    free(markStack_);
dvander@52
    65
}
dvander@52
    66
dvander@49
    67
Map *
dvander@49
    68
Heap::allocateMap(MapKind kind, size_t bytes)
dvander@49
    69
{
dvander@11
    70
    size_t actualBytes = Align(bytes, kObjectAlignment);
dvander@11
    71
dvander@11
    72
    Address addr = mapSpace_.allocate(actualBytes);
dvander@11
    73
    if (!addr) {
dvander@69
    74
        reportOutOfMemory();
dvander@11
    75
        return NULL;
dvander@11
    76
    }
dvander@11
    77
dvander@11
    78
    // Note that we use a NULL type initially, and reinterpret-cast to get
dvander@11
    79
    // the Type pointer, so we can allocate the self-referencing meta type.
dvander@11
    80
    Object *obj = InitAddressIntoObject(addr, NULL);
dvander@11
    81
    Map *map = reinterpret_cast<Map *>(obj);
dvander@11
    82
    map->setKind(kind);
dvander@49
    83
    return map;
dvander@49
    84
}
dvander@49
    85
dvander@52
    86
// To avoid marking recursively, we use a worklist to manage the marking
dvander@52
    87
// process. Algorithm:
dvander@52
    88
//
dvander@52
    89
// (1) While the mark stack is not empty:
dvander@52
    90
//  (a) Remove object |o| from the mark stack.
dvander@52
    91
//  (b) For each reference |p| in object |o|,
dvander@52
    92
//   (i)  Mark |p| as Black.
dvander@52
    93
//   (ii) Push |p| onto the mark stack.
dvander@52
    94
//   (iv) If the mark stack overflowde, mark |p| as Grey.
dvander@52
    95
//
dvander@52
    96
// We run this algorithm after pushing every root object onto the mark stack.
dvander@52
    97
// After all roots have been processed, it may be that the mark stack
dvander@52
    98
// overflowed. In this case, we use the following algorithm:
dvander@52
    99
//
dvander@52
   100
//  (1) while the mark stack has overflowed:
dvander@52
   101
//   (a) Search the heap for grey objects, adding as many as possible to the
dvander@52
   102
//       mark stack.
dvander@52
   103
//   (b) Run the mark-stack-emptying algorithm above.
dvander@52
   104
//
dvander@52
   105
// This algorithm is guaranteed to terminate, because even if there are more
dvander@52
   106
// grey objects than mark stack capacity, we eventually all objects will
dvander@52
   107
// become grey, and grey objects are not re-pushed onto the mark stack. Thus,
dvander@52
   108
// eventually, all objects will become black.
dvander@52
   109
//
dvander@59
   110
namespace ke {
dvander@52
   111
class MarkStack
dvander@52
   112
{
dvander@52
   113
    Object **base_;
dvander@52
   114
    Object **top_;
dvander@52
   115
    Object **end_;
dvander@52
   116
    bool overflowed_;
dvander@52
   117
dvander@52
   118
  public:
dvander@52
   119
    MarkStack(Object **base, Object **end)
dvander@52
   120
      : base_(base),
dvander@52
   121
        top_(base),
dvander@52
   122
        end_(end),
dvander@52
   123
        overflowed_(false)
dvander@52
   124
    {
dvander@52
   125
    }
dvander@52
   126
dvander@52
   127
    void pushBlack(Object *ptr) {
dvander@53
   128
        assert(HeapChunk::IsBlack(HeapChunk::GetMarkBit(ptr)));
dvander@52
   129
        if (top_ >= end_) {
dvander@52
   130
            MarkBit bit = HeapChunk::GetMarkBit(ptr);
dvander@52
   131
            HeapChunk::BlackToGrey(bit);
dvander@52
   132
            overflowed_ = true;
dvander@52
   133
            return;
dvander@52
   134
        }
dvander@52
   135
        *top_++ = ptr;
dvander@52
   136
    }
dvander@52
   137
dvander@52
   138
    Object *pop() {
dvander@52
   139
        assert(!empty());
dvander@52
   140
        return *--top_;
dvander@52
   141
    }
dvander@52
   142
    bool overflowed() const {
dvander@52
   143
        return overflowed_;
dvander@52
   144
    }
dvander@52
   145
    bool empty() const {
dvander@52
   146
        return base_ == top_;
dvander@52
   147
    }
dvander@53
   148
    void reset() {
dvander@53
   149
        assert(empty());
dvander@53
   150
        overflowed_ = true;
dvander@53
   151
    }
dvander@52
   152
};
dvander@59
   153
} // namespace ke
dvander@52
   154
dvander@52
   155
// Iterates over an object's body, pushing its referenced objects onto the
dvander@52
   156
// mark stack.
dvander@52
   157
class MarkingVisitor : public ObjectVisitor<VirtualObjectVisitor>
dvander@52
   158
{
dvander@52
   159
    MarkStack &stack_;
dvander@52
   160
dvander@52
   161
  public:
dvander@52
   162
    MarkingVisitor(MarkStack &stack)
dvander@52
   163
      : stack_(stack)
dvander@52
   164
    {
dvander@52
   165
    }
dvander@52
   166
dvander@52
   167
    void visit(Object **ptr) {
dvander@52
   168
        if (!*ptr)
dvander@52
   169
            return;
dvander@52
   170
dvander@52
   171
        Object *obj = *ptr;
dvander@52
   172
dvander@52
   173
        // If the object is grey, we'll find it later when searching the heap.
dvander@52
   174
        // If the object is black, it's already been scsanned.
dvander@52
   175
        MarkBit bit = HeapChunk::GetMarkBit(obj);
dvander@52
   176
        if (HeapChunk::IsBlackOrGrey(bit))
dvander@52
   177
            return;
dvander@52
   178
dvander@52
   179
        // Place the object in the stack and optimistically mark it black. If
dvander@52
   180
        // the stack overflows, it'll become grey instead.
dvander@52
   181
        HeapChunk::WhiteToBlack(bit);
dvander@52
   182
        stack_.pushBlack(obj);
dvander@52
   183
    }
dvander@52
   184
};
dvander@52
   185
dvander@52
   186
static void
dvander@52
   187
ProcessMarkStack(MarkStack &stack)
dvander@52
   188
{
dvander@52
   189
    MarkingVisitor visitor(stack);
dvander@52
   190
dvander@52
   191
    while (!stack.empty()) {
dvander@52
   192
        Object *obj = stack.pop();
dvander@52
   193
        assert(HeapChunk::IsBlack(HeapChunk::GetMarkBit(obj)));
dvander@52
   194
dvander@52
   195
        visitor.iterate(obj);
dvander@52
   196
    }
dvander@52
   197
}
dvander@52
   198
dvander@52
   199
class RootMarkingVisitor : public VirtualObjectVisitor
dvander@52
   200
{
dvander@52
   201
    MarkStack &stack_;
dvander@52
   202
    MarkingVisitor visitor_;
dvander@52
   203
dvander@52
   204
  public:
dvander@52
   205
    RootMarkingVisitor(MarkStack &stack)
dvander@52
   206
      : stack_(stack),
dvander@52
   207
        visitor_(stack)
dvander@52
   208
    {
dvander@52
   209
    }
dvander@52
   210
dvander@52
   211
    void visit(Object **ptr) {
dvander@52
   212
        // Flush the stack after every new root. Note that the root gets pushed
dvander@52
   213
        // as black; that's okay. If the mark stack overflows, its children are
dvander@52
   214
        // guaranteed to be at least grey.
dvander@52
   215
        visitor_.visit(ptr);
dvander@52
   216
        ProcessMarkStack(stack_);
dvander@52
   217
    }
dvander@52
   218
};
dvander@52
   219
dvander@53
   220
typedef ChunkObjectIteratorBase<ObjectSizeEvaluator> ChunkObjectIterator;
dvander@53
   221
dvander@53
   222
static inline bool
dvander@53
   223
EnqueueGreyObject(MarkStack &stack, Object *obj)
dvander@53
   224
{
dvander@53
   225
    assert(!stack.overflowed());
dvander@53
   226
    MarkBit bit = HeapChunk::GetMarkBit(obj);
dvander@53
   227
    if (HeapChunk::IsGrey(bit)) {
dvander@53
   228
        HeapChunk::GreyToBlack(bit);
dvander@53
   229
        stack.pushBlack(obj);
dvander@53
   230
        return !stack.overflowed();
dvander@53
   231
    }
dvander@53
   232
    return true;
dvander@53
   233
}
dvander@53
   234
dvander@52
   235
static void
dvander@53
   236
EnqueueGreyObjectsInChunks(MarkStack &stack, ChunkObjectIterator iter)
dvander@52
   237
{
dvander@53
   238
    for (Object *obj = iter.next(); obj; obj = iter.next()) {
dvander@53
   239
        if (!EnqueueGreyObject(stack, obj))
dvander@53
   240
            return;
dvander@53
   241
    }
dvander@53
   242
}
dvander@53
   243
dvander@53
   244
static void
dvander@53
   245
EnqueueGreyObjectsInLargeChunks(MarkStack &stack, HeapChunk *first)
dvander@53
   246
{
dvander@53
   247
    for (HeapChunk *chunk = first; chunk; chunk = chunk->next()) {
dvander@53
   248
        Object *obj = reinterpret_cast<Object *>(chunk->first());
dvander@53
   249
        if (!EnqueueGreyObject(stack, obj))
dvander@53
   250
            return;
dvander@53
   251
    }
dvander@52
   252
}
dvander@52
   253
dvander@52
   254
void
dvander@52
   255
Heap::findGreyObjects(MarkStack &stack)
dvander@52
   256
{
dvander@53
   257
    EnqueueGreyObjectsInChunks(stack, lifoSpace_.begin(ObjectSizeEvaluator()));
dvander@53
   258
    if (stack.overflowed())
dvander@53
   259
        return;
dvander@53
   260
dvander@53
   261
    EnqueueGreyObjectsInChunks(stack, mapSpace_.begin(ObjectSizeEvaluator()));
dvander@53
   262
    if (stack.overflowed())
dvander@53
   263
        return;
dvander@53
   264
    
dvander@53
   265
    EnqueueGreyObjectsInChunks(stack, oldSpace_.begin(ObjectSizeEvaluator()));
dvander@53
   266
    if (stack.overflowed())
dvander@53
   267
        return;
dvander@53
   268
dvander@53
   269
    EnqueueGreyObjectsInLargeChunks(stack, largeSpace_.first());
dvander@53
   270
    if (stack.overflowed())
dvander@53
   271
        return;
dvander@52
   272
}
dvander@52
   273
dvander@49
   274
void
dvander@51
   275
Heap::mark()
dvander@49
   276
{
dvander@52
   277
    Object **qbase = reinterpret_cast<Object **>(markStack_);
dvander@52
   278
    Object **qend = qbase + (kMarkStackSize / sizeof(Object *));
dvander@52
   279
    MarkStack stack(qbase, qend);
dvander@52
   280
    RootMarkingVisitor visitor(stack);
dvander@52
   281
dvander@52
   282
    // Attempt to mark all objects reachable from the root set.
dvander@52
   283
    zone_->accept(&visitor);
dvander@52
   284
    assert(stack.empty());
dvander@52
   285
dvander@52
   286
    // If the stack overflowed, we need to look through the entire heap for
dvander@53
   287
    // grey objects to rescan. We keep doing this until there are no grey
dvander@53
   288
    // objects left.
dvander@53
   289
    while (stack.overflowed()) {
dvander@53
   290
        stack.reset();
dvander@53
   291
        findGreyObjects(stack);
dvander@53
   292
        ProcessMarkStack(stack);
dvander@53
   293
    }
dvander@53
   294
}
dvander@53
   295
dvander@53
   296
// Use the mark bits to scan for live objects, and mark the regions in
dvander@53
   297
// between them as free.
dvander@53
   298
static void
dvander@53
   299
SweepOldSpaceChunk(OldSpace *space, HeapChunk *chunk)
dvander@53
   300
{
dvander@53
   301
    uint32 *markbits = chunk->markBits();
dvander@53
   302
    uint32 *markbitsEnd = markbits + HeapChunk::kMarkWords;
dvander@53
   303
    Address freeStart = chunk->first();
dvander@53
   304
dvander@53
   305
    // Scoot up to the first usable markword. Since memory is reserved for
dvander@53
   306
    // mark bits, not all addresses are allocatable, so quite a few of the
dvander@53
   307
    // initial mark bits are wasted. (1KB of mark bytes on x86.)
dvander@53
   308
    markbits = HeapChunk::GetMarkBit(reinterpret_cast<Object *>(chunk->first())).word();
dvander@53
   309
dvander@53
   310
    uint32 bits = *markbits;
dvander@53
   311
    for (;;) {
dvander@53
   312
        while (bits) {
dvander@53
   313
            uint32 bit = FindRightmostBit(bits);
dvander@53
   314
            Address object = HeapChunk::FromMarkBit(chunk, markbits, bit);
dvander@72
   315
dvander@72
   316
            assert(object >= freeStart);
dvander@53
   317
            if (freeStart != object)
dvander@53
   318
                space->markRegionFree(freeStart, object - freeStart);
dvander@72
   319
dvander@53
   320
            Object *obj = reinterpret_cast<Object *>(object);
dvander@53
   321
            freeStart = object + SizeOfObject(obj, obj->map());
dvander@53
   322
dvander@72
   323
            // Since black is 10, we never need to clear the next bit.
dvander@53
   324
            bits &= ~(1 << bit);
dvander@53
   325
        }
dvander@53
   326
        markbits++;
dvander@53
   327
        bits = *markbits;
dvander@53
   328
        if (markbits >= markbitsEnd)
dvander@53
   329
            break;
dvander@53
   330
    }
dvander@53
   331
dvander@53
   332
    if (freeStart == chunk->base())
dvander@53
   333
        chunk->setEmpty();
dvander@53
   334
    else if (freeStart != chunk->end())
dvander@53
   335
        space->markRegionFree(freeStart, chunk->end() - freeStart);
dvander@53
   336
}
dvander@53
   337
dvander@53
   338
static void
dvander@53
   339
SweepOldSpace(OldSpace *space)
dvander@53
   340
{
dvander@53
   341
    space->resetFreeList();
dvander@53
   342
    for (HeapChunk *chunk = space->first(); chunk; chunk = chunk->next()) {
dvander@53
   343
        SweepOldSpaceChunk(space, chunk);
dvander@53
   344
        chunk->resetMarkBits();
dvander@53
   345
    }
dvander@53
   346
    space->freeUnusedPages();
dvander@53
   347
}
dvander@53
   348
dvander@53
   349
void
dvander@53
   350
Heap::sweep()
dvander@53
   351
{
dvander@54
   352
    // Before destroying mark bits, sweep weak roots.
dvander@240
   353
    zone_->sweepWeakRoots();
dvander@54
   354
dvander@53
   355
    // Note: we don't bother sweeping the LIFO space.
dvander@53
   356
    SweepOldSpace(&mapSpace_);
dvander@53
   357
    SweepOldSpace(&oldSpace_);
dvander@53
   358
    largeSpace_.sweep();
dvander@49
   359
}
dvander@49
   360
dvander@51
   361
void
dvander@51
   362
Heap::reclaim()
dvander@51
   363
{
dvander@53
   364
    mark();
dvander@53
   365
    sweep();
dvander@51
   366
}
dvander@69
   367
dvander@69
   368
void
dvander@69
   369
Heap::reportOutOfMemory()
dvander@69
   370
{
dvander@69
   371
    zone_->reportOutOfMemory();
dvander@69
   372
}