DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (27a812186ff4)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sw=4 et tw=78:
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef gc_Nursery_h
#define gc_Nursery_h

#ifdef JSGC_GENERATIONAL

#include "jsalloc.h"
#include "jspubtd.h"

#include "ds/BitArray.h"
#include "gc/Heap.h"
#include "js/GCAPI.h"
#include "js/HashTable.h"
#include "js/HeapAPI.h"
#include "js/Value.h"
#include "js/Vector.h"

namespace JS {
struct Zone;
}

namespace js {

class ObjectElements;
class HeapSlot;

namespace gc {
class Cell;
class MinorCollectionTracer;
} /* namespace gc */

namespace types {
struct TypeObject;
}

namespace jit {
class CodeGenerator;
class MacroAssembler;
class ICStubCompiler;
class BaselineCompiler;
}

class Nursery
{
  public:
    static const int NumNurseryChunks = 16;
    static const int LastNurseryChunk = NumNurseryChunks - 1;
    static const size_t Alignment = gc::ChunkSize;
    static const size_t NurserySize = gc::ChunkSize * NumNurseryChunks;

    explicit Nursery(JSRuntime* rt)
      : runtime_(rt),
        position_(0),
        currentStart_(0),
        currentEnd_(0),
        currentChunk_(0),
        numActiveChunks_(0)
    {}
    ~Nursery();

    bool init();

    void enable();
    void disable();
    bool isEnabled() const { return numActiveChunks_ != 0; }

    /* Return true if no allocations have been made since the last collection. */
    bool isEmpty() const;

    template <typename T>
    MOZ_ALWAYS_INLINE bool isInside(const T* p) const {
        return gc::IsInsideNursery((JS::shadow::Runtime*)runtime_, p);
    }

    /*
     * Allocate and return a pointer to a new GC object with its |slots|
     * pointer pre-filled. Returns nullptr if the Nursery is full.
     */
    JSObject* allocateObject(JSContext* cx, size_t size, size_t numDynamic);

    /* Allocate a slots array for the given object. */
    HeapSlot* allocateSlots(JSContext* cx, JSObject* obj, uint32_t nslots);

    /* Allocate an elements vector for the given object. */
    ObjectElements* allocateElements(JSContext* cx, JSObject* obj, uint32_t nelems);

    /* Resize an existing slots array. */
    HeapSlot* reallocateSlots(JSContext* cx, JSObject* obj, HeapSlot* oldSlots,
                              uint32_t oldCount, uint32_t newCount);

    /* Resize an existing elements vector. */
    ObjectElements* reallocateElements(JSContext* cx, JSObject* obj, ObjectElements* oldHeader,
                                       uint32_t oldCount, uint32_t newCount);

    /* Free a slots array. */
    void freeSlots(JSContext* cx, HeapSlot* slots);

    /* Add a slots to our tracking list if it is out-of-line. */
    void notifyInitialSlots(gc::Cell* cell, HeapSlot* slots);

    /* Add elements to our tracking list if it is out-of-line. */
    void notifyNewElements(gc::Cell* cell, ObjectElements* elements);

    /* Remove elements to our tracking list if it is out-of-line. */
    void notifyRemovedElements(gc::Cell* cell, ObjectElements* oldElements);

    typedef Vector<types::TypeObject*, 0, SystemAllocPolicy> TypeObjectList;

    /*
     * Do a minor collection, optionally specifying a list to store types which
     * should be pretenured afterwards.
     */
    void collect(JSRuntime* rt, JS::gcreason::Reason reason, TypeObjectList* pretenureTypes);

    /*
     * Check if the thing at |*ref| in the Nursery has been forwarded. If so,
     * sets |*ref| to the new location of the object and returns true. Otherwise
     * returns false and leaves |*ref| unset.
     */
    template <typename T>
    MOZ_ALWAYS_INLINE bool getForwardedPointer(T** ref);

    /* Forward a slots/elements pointer stored in an Ion frame. */
    void forwardBufferPointer(HeapSlot** pSlotsElems);

    size_t sizeOfHeap() { return start() ? NurserySize : 0; }

#ifdef JS_GC_ZEAL
    /*
     * In debug and zeal builds, these bytes indicate the state of an unused
     * segment of nursery-allocated memory.
     */
    static const uint8_t FreshNursery = 0x2a;
    static const uint8_t SweptNursery = 0x2b;
    static const uint8_t AllocatedThing = 0x2c;
    void enterZealMode() {
        if (isEnabled())
            numActiveChunks_ = NumNurseryChunks;
    }
    void leaveZealMode() {
        if (isEnabled()) {
            JS_ASSERT(isEmpty());
            setCurrentChunk(0);
        }
    }
#endif

  private:
    /*
     * The start and end pointers are stored under the runtime so that we can
     * inline the isInsideNursery check into embedder code. Use the start()
     * and heapEnd() functions to access these values.
     */
    JSRuntime* runtime_;

    /* Pointer to the first unallocated byte in the nursery. */
    uintptr_t position_;

    /* Pointer to the logic start of the Nursery. */
    uintptr_t currentStart_;

    /* Pointer to the last byte of space in the current chunk. */
    uintptr_t currentEnd_;

    /* The index of the chunk that is currently being allocated from. */
    int currentChunk_;

    /* The index after the last chunk that we will allocate from. */
    int numActiveChunks_;

    /*
     * The set of externally malloced slots potentially kept live by objects
     * stored in the nursery. Any external slots that do not belong to a
     * tenured thing at the end of a minor GC must be freed.
     */
    typedef HashSet<HeapSlot*, PointerHasher<HeapSlot*, 3>, SystemAllocPolicy> HugeSlotsSet;
    HugeSlotsSet hugeSlots;

    /* The maximum number of slots allowed to reside inline in the nursery. */
    static const size_t MaxNurserySlots = 128;

    /* The amount of space in the mapped nursery available to allocations. */
    static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer);

    struct NurseryChunkLayout {
        char data[NurseryChunkUsableSize];
        gc::ChunkTrailer trailer;
        uintptr_t start() { return uintptr_t(&data); }
        uintptr_t end() { return uintptr_t(&trailer); }
    };
    static_assert(sizeof(NurseryChunkLayout) == gc::ChunkSize,
                  "Nursery chunk size must match gc::Chunk size.");
    NurseryChunkLayout& chunk(int index) const {
        JS_ASSERT(index < NumNurseryChunks);
        JS_ASSERT(start());
        return reinterpret_cast<NurseryChunkLayout*>(start())[index];
    }

    MOZ_ALWAYS_INLINE uintptr_t start() const {
        JS_ASSERT(runtime_);
        return ((JS::shadow::Runtime*)runtime_)->gcNurseryStart_;
    }

    MOZ_ALWAYS_INLINE uintptr_t heapEnd() const {
        JS_ASSERT(runtime_);
        return ((JS::shadow::Runtime*)runtime_)->gcNurseryEnd_;
    }

    MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) {
        JS_ASSERT(chunkno < NumNurseryChunks);
        JS_ASSERT(chunkno < numActiveChunks_);
        currentChunk_ = chunkno;
        position_ = chunk(chunkno).start();
        currentStart_ = chunk(0).start();
        currentEnd_ = chunk(chunkno).end();
    }

    MOZ_ALWAYS_INLINE uintptr_t allocationEnd() const {
        JS_ASSERT(numActiveChunks_ > 0);
        return chunk(numActiveChunks_ - 1).end();
    }

    MOZ_ALWAYS_INLINE bool isFullyGrown() const {
        return numActiveChunks_ == NumNurseryChunks;
    }

    MOZ_ALWAYS_INLINE uintptr_t currentEnd() const {
        JS_ASSERT(runtime_);
        JS_ASSERT(currentEnd_ == chunk(currentChunk_).end());
        return currentEnd_;
    }
    void* addressOfCurrentEnd() const {
        JS_ASSERT(runtime_);
        return (void*)&currentEnd_;
    }

    uintptr_t position() const { return position_; }
    void* addressOfPosition() const { return (void*)&position_; }

    JSRuntime* runtime() const { return runtime_; }

    /* Allocates and registers external slots with the nursery. */
    HeapSlot* allocateHugeSlots(JSContext* cx, size_t nslots);

    /* Allocates a new GC thing from the tenured generation during minor GC. */
    void* allocateFromTenured(JS::Zone* zone, gc::AllocKind thingKind);

    struct TenureCountCache;

    /* Common internal allocator function. */
    void* allocate(size_t size);

    /*
     * Move the object at |src| in the Nursery to an already-allocated cell
     * |dst| in Tenured.
     */
    void collectToFixedPoint(gc::MinorCollectionTracer* trc, TenureCountCache& tenureCounts);
    MOZ_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer* trc, JSObject* src);
    MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer* trc, HeapSlot* vp, uint32_t nslots);
    MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer* trc, HeapSlot* vp, HeapSlot* end);
    MOZ_ALWAYS_INLINE void markSlot(gc::MinorCollectionTracer* trc, HeapSlot* slotp);
    void* moveToTenured(gc::MinorCollectionTracer* trc, JSObject* src);
    size_t moveObjectToTenured(JSObject* dst, JSObject* src, gc::AllocKind dstKind);
    size_t moveElementsToTenured(JSObject* dst, JSObject* src, gc::AllocKind dstKind);
    size_t moveSlotsToTenured(JSObject* dst, JSObject* src, gc::AllocKind dstKind);

    /* Handle relocation of slots/elements pointers stored in Ion frames. */
    void setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots, uint32_t nslots);
    void setElementsForwardingPointer(ObjectElements* oldHeader, ObjectElements* newHeader,
                                      uint32_t nelems);

    /* Free malloced pointers owned by freed things in the nursery. */
    void freeHugeSlots(JSRuntime* rt);

    /*
     * Frees all non-live nursery-allocated things at the end of a minor
     * collection.
     */
    void sweep(JSRuntime* rt);

    /* Change the allocable space provided by the nursery. */
    void growAllocableSpace();
    void shrinkAllocableSpace();

    static void MinorGCCallback(JSTracer* trc, void** thingp, JSGCTraceKind kind);

    friend class gc::MinorCollectionTracer;
    friend class jit::CodeGenerator;
    friend class jit::MacroAssembler;
    friend class jit::ICStubCompiler;
    friend class jit::BaselineCompiler;
};

} /* namespace js */

#endif /* JSGC_GENERATIONAL */
#endif /* gc_Nursery_h */