tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Nursery.h (28155B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sw=2 et tw=80:
      3 *
      4 * This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      6 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #ifndef gc_Nursery_h
      9 #define gc_Nursery_h
     10 
     11 #include "mozilla/EnumeratedArray.h"
     12 #include "mozilla/TimeStamp.h"
     13 
     14 #include <tuple>
     15 
     16 #include "ds/LifoAlloc.h"
     17 #include "ds/SlimLinkedList.h"
     18 #include "gc/Allocator.h"
     19 #include "gc/GCEnum.h"
     20 #include "gc/GCProbes.h"
     21 #include "gc/Heap.h"
     22 #include "gc/Pretenuring.h"
     23 #include "js/AllocPolicy.h"
     24 #include "js/Class.h"
     25 #include "js/GCAPI.h"
     26 #include "js/GCVector.h"
     27 #include "js/HeapAPI.h"
     28 #include "js/TypeDecls.h"
     29 #include "js/UniquePtr.h"
     30 #include "js/Utility.h"
     31 #include "js/Vector.h"
     32 
     33 #define FOR_EACH_NURSERY_PROFILE_TIME(_)      \
     34  /* Key                       Header text */ \
     35  _(Total, "total")                           \
     36  _(TraceValues, "mkVals")                    \
     37  _(TraceCells, "mkClls")                     \
     38  _(TraceSlots, "mkSlts")                     \
     39  _(TraceWasmAnyRefs, "mkWars")               \
     40  _(TraceWholeCells, "mcWCll")                \
     41  _(TraceGenericEntries, "mkGnrc")            \
     42  _(CheckHashTables, "ckTbls")                \
     43  _(MarkRuntime, "mkRntm")                    \
     44  _(MarkDebugger, "mkDbgr")                   \
     45  _(TraceWeakMaps, "trWkMp")                  \
     46  _(SweepCaches, "swpCch")                    \
     47  _(CollectToObjFP, "colObj")                 \
     48  _(CollectToStrFP, "colStr")                 \
     49  _(ObjectsTenuredCallback, "tenCB")          \
     50  _(Sweep, "sweep")                           \
     51  _(UpdateJitActivations, "updtIn")           \
     52  _(FreeMallocedBuffers, "frSlts")            \
     53  _(ClearNursery, "clear")                    \
     54  _(PurgeStringToAtomCache, "pStoA")          \
     55  _(Pretenure, "pretnr")
     56 
     57 template <typename T>
     58 class SharedMem;
     59 
     60 namespace mozilla {
     61 class StringBuffer;
     62 };
     63 
     64 namespace js {
     65 
     66 struct StringStats;
     67 class AutoLockGCBgAlloc;
     68 class ObjectElements;
     69 struct NurseryChunk;
     70 class HeapSlot;
     71 class JSONPrinter;
     72 class MapObject;
     73 class NurseryDecommitTask;
     74 class NurserySweepTask;
     75 class SetObject;
     76 class JS_PUBLIC_API Sprinter;
     77 class WeakMapBase;
     78 
     79 namespace gc {
     80 
     81 class AutoGCSession;
     82 struct Cell;
     83 class GCSchedulingTunables;
     84 struct LargeBuffer;
     85 class StoreBuffer;
     86 class TenuringTracer;
     87 
     88 }  // namespace gc
     89 
     90 class Nursery {
     91 public:
     92  explicit Nursery(gc::GCRuntime* gc);
     93  ~Nursery();
     94 
     95  [[nodiscard]] bool init(AutoLockGCBgAlloc& lock);
     96 
     97  void enable();
     98  void disable();
     99  bool isEnabled() const { return capacity() != 0; }
    100 
    101  void enableStrings();
    102  void disableStrings();
    103  bool canAllocateStrings() const { return canAllocateStrings_; }
    104 
    105  void enableBigInts();
    106  void disableBigInts();
    107  bool canAllocateBigInts() const { return canAllocateBigInts_; }
    108 
    109  void setSemispaceEnabled(bool enabled);
    110  bool semispaceEnabled() const { return semispaceEnabled_; }
    111 
    112  // Return true if no allocations have been made since the last collection.
    113  bool isEmpty() const;
    114 
    115  // Check whether an arbitrary pointer is within the nursery. This is
    116  // slower than IsInsideNursery(Cell*), but works on all types of pointers.
    117  bool isInside(gc::Cell* cellp) const = delete;
    118  inline bool isInside(const void* p) const;
    119 
    120  template <typename T>
    121  inline bool isInside(const SharedMem<T>& p) const;
    122 
    123  // Allocate and return a pointer to a new GC thing. Returns nullptr if the
    124  // Nursery is full.
    125  void* allocateCell(gc::AllocSite* site, size_t size, JS::TraceKind kind);
    126 
    127  // Allocate and return a pointer to a new GC thing. Returns nullptr if the
    128  // handleAllocationFailure() needs to be called before retrying.
    129  inline void* tryAllocateCell(gc::AllocSite* site, size_t size,
    130                               JS::TraceKind kind);
    131 
    132  // Attempt to handle the failure of tryAllocate. Returns a GCReason if minor
    133  // GC is required, or NO_REASON if the failure was handled and allocation will
    134  // now succeed.
    135  [[nodiscard]] JS::GCReason handleAllocationFailure();
    136 
    137  static size_t nurseryCellHeaderSize() {
    138    return sizeof(gc::NurseryCellHeader);
    139  }
    140 
    141  // Allocate a buffer for a given zone, using the nursery if possible. Returns
    142  // <buffer, isMalloced> so the caller can register the buffer if
    143  // needed. Returns false in |isMalloced| if the allocation fails.
    144  //
    145  // Use the following API if the owning Cell is already known.
    146  std::tuple<void*, bool> allocNurseryOrMallocBuffer(JS::Zone* zone,
    147                                                     size_t nbytes,
    148                                                     arena_id_t arenaId);
    149  void* allocateInternalBuffer(JS::Zone* zone, size_t nbytes);
    150 
    151  // Like allocNurseryOrMallocBuffer, but returns nullptr if the buffer can't
    152  // be allocated in the nursery.
    153  void* tryAllocateNurseryBuffer(JS::Zone* zone, size_t nbytes,
    154                                 arena_id_t arenaId);
    155 
    156  // Allocate a buffer for a given Cell, using the nursery if possible and
    157  // owner is in the nursery.
    158  void* allocNurseryOrMallocBuffer(JS::Zone* zone, gc::Cell* owner,
    159                                   size_t nbytes, arena_id_t arenaId);
    160  void* allocateBuffer(JS::Zone* zone, gc::Cell* owner, size_t nbytes,
    161                       size_t maxNurserySize);
    162 
    163  // Allocate a zero-initialized buffer for a given zone, using the nursery if
    164  // possible. If the buffer isn't allocated in the nursery, the given arena is
    165  // used. Returns <buffer, isMalloced>. Returns false in |isMalloced| if the
    166  // allocation fails.
    167  std::tuple<void*, bool> allocateZeroedBuffer(JS::Zone* zone, size_t nbytes,
    168                                               arena_id_t arena);
    169 
    170  // Allocate a zero-initialized buffer for a given Cell, using the nursery if
    171  // possible and |owner| is in the nursery. If the buffer isn't allocated in
    172  // the nursery, the given arena is used.
    173  void* allocateZeroedBuffer(gc::Cell* owner, size_t nbytes, arena_id_t arena);
    174 
    175  // Resize an existing buffer.
    176  void* reallocNurseryOrMallocBuffer(JS::Zone* zone, gc::Cell* cell,
    177                                     void* oldBuffer, size_t oldBytes,
    178                                     size_t newBytes, arena_id_t arena);
    179 
    180  // Resize an existing buffer.
    181  void* reallocateBuffer(JS::Zone* zone, gc::Cell* cell, void* oldBuffer,
    182                         size_t oldBytes, size_t newBytes,
    183                         size_t maxNurserySize);
    184 
    185  // Free an existing buffer.
    186  void freeBuffer(JS::Zone* zone, gc::Cell* cell, void* buffer, size_t bytes);
    187 
    188  // The maximum number of bytes allowed to reside in nursery buffers.
    189  static const size_t MaxNurseryBufferSize = 1024;
    190 
    191  // Do a minor collection.
    192  void collect(JS::GCOptions options, JS::GCReason reason);
    193 
    194  // If the thing at |*ref| in the Nursery has been forwarded, set |*ref| to
    195  // the new location and return true. Otherwise return false and leave
    196  // |*ref| unset.
    197  [[nodiscard]] MOZ_ALWAYS_INLINE static bool getForwardedPointer(
    198      js::gc::Cell** ref);
    199 
    200  // Forward a slots/elements pointer stored in an Ion frame.
    201  void forwardBufferPointer(uintptr_t* pSlotsElems);
    202 
    203  inline void maybeSetForwardingPointer(JSTracer* trc, void* oldData,
    204                                        void* newData, bool direct);
    205  inline void setForwardingPointerWhileTenuring(void* oldData, void* newData,
    206                                                bool direct);
    207 
    208  // Handle an external buffer when a cell is promoted. Updates the pointer to
    209  // the (possibly moved) buffer and returns whether it was moved.
    210  // bytesUsed can be less than bytesCapacity if not all bytes need to be copied
    211  // when the buffer is moved.
    212  enum WasBufferMoved : bool { BufferNotMoved = false, BufferMoved = true };
    213  WasBufferMoved maybeMoveRawNurseryOrMallocBufferOnPromotion(
    214      void** bufferp, gc::Cell* owner, size_t bytesUsed, size_t bytesCapacity,
    215      MemoryUse use, arena_id_t arena);
    216  template <typename T>
    217  WasBufferMoved maybeMoveNurseryOrMallocBufferOnPromotion(
    218      T** bufferp, gc::Cell* owner, size_t bytesUsed, size_t bytesCapacity,
    219      MemoryUse use, arena_id_t arena) {
    220    return maybeMoveRawNurseryOrMallocBufferOnPromotion(
    221        reinterpret_cast<void**>(bufferp), owner, bytesUsed, bytesCapacity, use,
    222        arena);
    223  }
    224  template <typename T>
    225  WasBufferMoved maybeMoveNurseryOrMallocBufferOnPromotion(T** bufferp,
    226                                                           gc::Cell* owner,
    227                                                           size_t nbytes,
    228                                                           MemoryUse use) {
    229    return maybeMoveNurseryOrMallocBufferOnPromotion(bufferp, owner, nbytes,
    230                                                     nbytes, use, MallocArena);
    231  }
    232 
    233  WasBufferMoved maybeMoveRawBufferOnPromotion(void** bufferp, gc::Cell* owner,
    234                                               size_t nbytes);
    235  template <typename T>
    236  WasBufferMoved maybeMoveBufferOnPromotion(T** bufferp, gc::Cell* owner,
    237                                            size_t nbytes) {
    238    return maybeMoveRawBufferOnPromotion(reinterpret_cast<void**>(bufferp),
    239                                         owner, nbytes);
    240  }
    241 
    242  // Register a malloced buffer that is held by a nursery object, which
    243  // should be freed at the end of a minor GC. Buffers are unregistered when
    244  // their owning objects are tenured.
    245  [[nodiscard]] bool registerMallocedBuffer(void* buffer, size_t nbytes);
    246 
    247  // Mark a malloced buffer as no longer needing to be freed.
    248  inline void removeMallocedBuffer(void* buffer, size_t nbytes);
    249 
    250  // Mark a malloced buffer as no longer needing to be freed during minor
    251  // GC. There's no need to account for the size here since all remaining
    252  // buffers will soon be freed.
    253  inline void removeMallocedBufferDuringMinorGC(void* buffer);
    254 
    255  [[nodiscard]] bool addedUniqueIdToCell(gc::Cell* cell) {
    256    MOZ_ASSERT(IsInsideNursery(cell));
    257    MOZ_ASSERT(isEnabled());
    258    return cellsWithUid_.append(cell);
    259  }
    260 
    261  [[nodiscard]] inline bool addStringBuffer(JSLinearString* s);
    262 
    263  [[nodiscard]] inline bool addExtensibleStringBuffer(
    264      JSLinearString* s, mozilla::StringBuffer* buffer,
    265      bool updateMallocBytes = true);
    266  inline void removeExtensibleStringBuffer(JSLinearString* s,
    267                                           bool updateMallocBytes = true);
    268 
    269  size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const;
    270 
    271  size_t totalCapacity() const;
    272  size_t totalCommitted() const;
    273 
    274 #ifdef JS_GC_ZEAL
    275  void enterZealMode();
    276  void leaveZealMode();
    277 #endif
    278 
    279  // Write profile time JSON on JSONPrinter.
    280  void renderProfileJSON(JSONPrinter& json) const;
    281 
    282  // Print header line for profile times.
    283  void printProfileHeader();
    284 
    285  // Print total profile times on shutdown.
    286  void printTotalProfileTimes();
    287 
    288  void* addressOfPosition() const { return (void**)&toSpace.position_; }
    289  static constexpr int32_t offsetOfCurrentEndFromPosition() {
    290    return offsetof(Nursery, toSpace.currentEnd_) -
    291           offsetof(Nursery, toSpace.position_);
    292  }
    293 
    294  void* addressOfNurseryAllocatedSites() {
    295    return pretenuringNursery.addressOfAllocatedSites();
    296  }
    297 
    298  void requestMinorGC(JS::GCReason reason);
    299 
    300  bool minorGCRequested() const {
    301    return minorGCTriggerReason_ != JS::GCReason::NO_REASON;
    302  }
    303  JS::GCReason minorGCTriggerReason() const { return minorGCTriggerReason_; }
    304 
    305  bool wantEagerCollection() const;
    306 
    307  bool enableProfiling() const { return enableProfiling_; }
    308 
    309  bool addMapWithNurseryIterators(MapObject* obj) {
    310    MOZ_ASSERT_IF(!mapsWithNurseryIterators_.empty(),
    311                  mapsWithNurseryIterators_.back() != obj);
    312    return mapsWithNurseryIterators_.append(obj);
    313  }
    314  bool addSetWithNurseryIterators(SetObject* obj) {
    315    MOZ_ASSERT_IF(!setsWithNurseryIterators_.empty(),
    316                  setsWithNurseryIterators_.back() != obj);
    317    return setsWithNurseryIterators_.append(obj);
    318  }
    319  bool addWeakMapWithNurseryEntries(WeakMapBase* wm) {
    320    MOZ_ASSERT_IF(!weakMapsWithNurseryEntries_.empty(),
    321                  weakMapsWithNurseryEntries_.back() != wm);
    322    return weakMapsWithNurseryEntries_.append(wm);
    323  }
    324 
    325  void joinSweepTask();
    326  void joinDecommitTask();
    327 
    328 #ifdef DEBUG
    329  bool sweepTaskIsIdle();
    330 #endif
    331 
    332  mozilla::TimeStamp collectionStartTime() {
    333    return startTimes_[ProfileKey::Total];
    334  }
    335 
    336  bool canCreateAllocSite() { return pretenuringNursery.canCreateAllocSite(); }
    337  void noteAllocSiteCreated() { pretenuringNursery.noteAllocSiteCreated(); }
    338  bool reportPretenuring() const { return pretenuringReportFilter_.enabled; }
    339  void maybeStopPretenuring(gc::GCRuntime* gc) {
    340    pretenuringNursery.maybeStopPretenuring(gc);
    341  }
    342 
    343  void setAllocFlagsForZone(JS::Zone* zone);
    344 
    345  bool shouldTenureEverything(JS::GCReason reason);
    346 
    347  inline bool inCollectedRegion(const gc::Cell* cell) const;
    348  inline bool inCollectedRegion(void* ptr) const;
    349 
    350  void trackMallocedBufferOnPromotion(void* buffer, gc::Cell* owner,
    351                                      size_t nbytes, MemoryUse use);
    352  void trackBufferOnPromotion(void* buffer, gc::Cell* owner, size_t nbytes);
    353 
    354  // Round a size in bytes to the nearest valid nursery size.
    355  static size_t roundSize(size_t size);
    356 
    357  inline void addMallocedBufferBytes(size_t nbytes);
    358  inline void removeMallocedBufferBytes(size_t nbytes);
    359 
    360  mozilla::TimeStamp lastCollectionEndTime() const;
    361 
    362  size_t capacity() const { return capacity_; }
    363 
    364 private:
    365  struct Space;
    366 
    367  enum class ProfileKey {
    368 #define DEFINE_TIME_KEY(name, text) name,
    369    FOR_EACH_NURSERY_PROFILE_TIME(DEFINE_TIME_KEY)
    370 #undef DEFINE_TIME_KEY
    371        KeyCount
    372  };
    373 
    374  using ProfileTimes = mozilla::EnumeratedArray<ProfileKey, mozilla::TimeStamp,
    375                                                size_t(ProfileKey::KeyCount)>;
    376  using ProfileDurations =
    377      mozilla::EnumeratedArray<ProfileKey, mozilla::TimeDuration,
    378                               size_t(ProfileKey::KeyCount)>;
    379 
    380  // Total number of chunks and the capacity of the current nursery
    381  // space. Chunks will be lazily allocated and added to the chunks array up to
    382  // this limit. After that the nursery must be collected. This limit may be
    383  // changed at the end of collection by maybeResizeNursery.
    384  uint32_t maxChunkCount() const {
    385    MOZ_ASSERT(toSpace.maxChunkCount_);
    386    return toSpace.maxChunkCount_;
    387  }
    388 
    389  // Number of allocated (ready to use) chunks.
    390  unsigned allocatedChunkCount() const { return toSpace.chunks_.length(); }
    391 
    392  uint32_t currentChunk() const { return toSpace.currentChunk_; }
    393  uint32_t startChunk() const { return toSpace.startChunk_; }
    394  uintptr_t startPosition() const { return toSpace.startPosition_; }
    395 
    396  // Used and free space both include chunk headers for that part of the
    397  // nursery.
    398  MOZ_ALWAYS_INLINE size_t usedSpace() const {
    399    return capacity() - freeSpace();
    400  }
    401  MOZ_ALWAYS_INLINE size_t freeSpace() const {
    402    MOZ_ASSERT(isEnabled());
    403    MOZ_ASSERT(currentChunk() < maxChunkCount());
    404    return (currentEnd() - position()) +
    405           (maxChunkCount() - currentChunk() - 1) * gc::ChunkSize;
    406  }
    407 
    408  // Calculate the promotion rate of the most recent minor GC.
    409  // The valid_for_tenuring parameter is used to return whether this
    410  // promotion rate is accurate enough (the nursery was full enough) to be
    411  // used for tenuring and other decisions.
    412  //
    413  // Must only be called if the previousGC data is initialised.
    414  double calcPromotionRate(bool* validForTenuring) const;
    415 
    416  NurseryChunk& chunk(unsigned index) const { return *toSpace.chunks_[index]; }
    417 
    418  // Set the allocation position to the start of a chunk. This sets
    419  // currentChunk_, position_ and currentEnd_ values as appropriate.
    420  void moveToStartOfChunk(unsigned chunkno);
    421 
    422  bool initFirstChunk(AutoLockGCBgAlloc& lock);
    423  void setCapacity(size_t newCapacity);
    424 
    425  void poisonAndInitCurrentChunk();
    426 
    427  void setCurrentEnd();
    428  void setStartToCurrentPosition();
    429 
    430  // Allocate another chunk.
    431  [[nodiscard]] bool allocateNextChunk(AutoLockGCBgAlloc& lock);
    432 
    433  uintptr_t position() const { return toSpace.position_; }
    434  uintptr_t currentEnd() const { return toSpace.currentEnd_; }
    435 
    436  MOZ_ALWAYS_INLINE bool isSubChunkMode() const;
    437 
    438  JSRuntime* runtime() const;
    439  gcstats::Statistics& stats() const;
    440 
    441  const js::gc::GCSchedulingTunables& tunables() const;
    442 
    443  void getAllocFlagsForZone(JS::Zone* zone, bool* allocObjectsOut,
    444                            bool* allocStringsOut, bool* allocBigIntsOut,
    445                            bool* allocGetterSettersOut);
    446  void updateAllZoneAllocFlags();
    447  void updateAllocFlagsForZone(JS::Zone* zone);
    448  void discardCodeAndSetJitFlagsForZone(JS::Zone* zone);
    449 
    450  void* allocate(size_t size);
    451 
    452  // Common internal allocator function. If this fails, call
    453  // handleAllocationFailure to see whether it's possible to retry.
    454  inline void* tryAllocate(size_t size);
    455 
    456  [[nodiscard]] bool moveToNextChunk();
    457 
    458  bool freeSpaceIsBelowEagerThreshold() const;
    459  bool isUnderused() const;
    460 
    461  struct CollectionResult {
    462    size_t tenuredBytes;
    463    size_t tenuredCells;
    464  };
    465  CollectionResult doCollection(gc::AutoGCSession& session,
    466                                JS::GCOptions options, JS::GCReason reason);
    467  void swapSpaces();
    468  void traceRoots(gc::AutoGCSession& session, gc::TenuringTracer& mover);
    469 
    470  size_t doPretenuring(JSRuntime* rt, JS::GCReason reason,
    471                       bool validPromotionRate, double promotionRate);
    472 
    473  // Handle relocation of slots/elements pointers stored in Ion frames.
    474  inline void setForwardingPointer(void* oldData, void* newData, bool direct);
    475 
    476  inline void setDirectForwardingPointer(void* oldData, void* newData);
    477  void setIndirectForwardingPointer(void* oldData, void* newData);
    478 
    479  inline void setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots,
    480                                        uint32_t nslots);
    481  inline void setElementsForwardingPointer(ObjectElements* oldHeader,
    482                                           ObjectElements* newHeader,
    483                                           uint32_t capacity);
    484 
    485 #ifdef DEBUG
    486  bool checkForwardingPointerInsideNursery(void* ptr);
    487 #endif
    488 
    489  // Discard pointers to objects that have been freed.
    490  void sweep();
    491 
    492  // In a minor GC, resets the start and end positions, the current chunk and
    493  // current position.
    494  void setNewExtentAndPosition();
    495 
    496  // the nursery on debug & nightly builds.
    497  void clear();
    498 
    499  void clearMapAndSetNurseryIterators();
    500  void sweepMapAndSetObjects();
    501 
    502  void traceWeakMaps(gc::TenuringTracer& trc);
    503  void sweepWeakMaps();
    504 
    505  void sweepStringsWithBuffer();
    506 
    507  void sweepBuffers();
    508 
    509  // Get per-space size limits.
    510  size_t maxSpaceSize() const;
    511  size_t minSpaceSize() const;
    512 
    513  // Change the allocable space provided by the nursery.
    514  void maybeResizeNursery(JS::GCOptions options, JS::GCReason reason);
    515  size_t targetSize(JS::GCOptions options, JS::GCReason reason);
    516  void clearRecentGrowthData();
    517  void growAllocableSpace(size_t newCapacity);
    518  void shrinkAllocableSpace(size_t newCapacity);
    519  void minimizeAllocableSpace();
    520 
    521  // Free the chunks starting at firstFreeChunk until the end of the chunks
    522  // vector. Shrinks the vector but does not update maxChunkCount().
    523  void freeChunksFrom(Space& space, unsigned firstFreeChunk);
    524 
    525  // During a semispace nursery collection, return whether a cell in fromspace
    526  // was in the tospace of the previous collection, meaning that it should be
    527  // tenured in this collection.
    528  inline bool shouldTenure(gc::Cell* cell);
    529 
    530  void sendTelemetry(JS::GCReason reason, mozilla::TimeDuration totalTime,
    531                     bool wasEmpty, double promotionRate,
    532                     size_t sitesPretenured);
    533 
    534  void printCollectionProfile(JS::GCReason reason, double promotionRate);
    535  void printDeduplicationData(js::StringStats& prev, js::StringStats& curr);
    536 
    537  // Profile recording and printing.
    538  void maybeClearProfileDurations();
    539  void startProfile(ProfileKey key);
    540  void endProfile(ProfileKey key);
    541  static void printProfileDurations(const ProfileDurations& times,
    542                                    Sprinter& sprinter);
    543 
    544  mozilla::TimeStamp collectionStartTime() const;
    545 
    546 private:
    547  using BufferRelocationOverlay = void*;
    548  using BufferSet = HashSet<void*, PointerHasher<void*>, SystemAllocPolicy>;
    549 
    550  struct Space {
    551    // Fields used during allocation fast path go first:
    552 
    553    // Pointer to the first unallocated byte in the nursery.
    554    uintptr_t position_ = 0;
    555 
    556    // Pointer to the last byte of space in the current chunk.
    557    uintptr_t currentEnd_ = 0;
    558 
    559    // Vector of allocated chunks to allocate from.
    560    Vector<NurseryChunk*, 0, SystemAllocPolicy> chunks_;
    561 
    562    // The index of the chunk that is currently being allocated from.
    563    uint32_t currentChunk_ = 0;
    564 
    565    // The maximum number of chunks to allocate based on capacity_.
    566    uint32_t maxChunkCount_ = 0;
    567 
    568    // These fields refer to the beginning of the nursery. They're normally 0
    569    // and chunk(0).start() respectively. Except when a generational GC zeal
    570    // mode is active, then they may be arbitrary (see Nursery::clear()).
    571    uint32_t startChunk_ = 0;
    572    uintptr_t startPosition_ = 0;
    573 
    574    // The set of malloc-allocated buffers owned by nursery objects. Any
    575    // buffers that do not belong to a promoted thing at the end of a minor GC
    576    // must be freed.
    577    BufferSet mallocedBuffers;
    578    size_t mallocedBufferBytes = 0;
    579 
    580    gc::ChunkKind kind;
    581 
    582    explicit Space(gc::ChunkKind kind);
    583 
    584    inline bool isEmpty() const;
    585    inline bool isInside(const void* p) const;
    586 
    587    // Return the logical offset within the nursery of an address in a nursery
    588    // chunk (chunks are discontiguous in memory).
    589    inline size_t offsetFromAddress(uintptr_t addr) const;
    590    inline size_t offsetFromExclusiveAddress(uintptr_t addr) const;
    591 
    592    void setKind(gc::ChunkKind newKind);
    593 
    594    void clear(Nursery* nursery);
    595    void moveToStartOfChunk(Nursery* nursery, unsigned chunkno);
    596    void setCurrentEnd(Nursery* nursery);
    597    void setStartToCurrentPosition();
    598    bool commitSubChunkRegion(size_t oldCapacity, size_t newCapacity);
    599    void decommitSubChunkRegion(Nursery* nursery, size_t oldCapacity,
    600                                size_t newCapacity);
    601 
    602 #ifdef DEBUG
    603    void checkKind(gc::ChunkKind expected) const;
    604    size_t findChunkIndex(uintptr_t chunkAddr) const;
    605 #endif
    606  };
    607 
    608  Space toSpace;
    609  Space fromSpace;
    610 
    611  gc::GCRuntime* const gc;
    612 
    613  // The current nursery capacity measured in bytes. It may grow up to this
    614  // value without a collection, allocating chunks on demand. This limit may be
    615  // changed by maybeResizeNursery() each collection. It includes chunk headers.
    616  size_t capacity_;
    617 
    618  uintptr_t tenureThreshold_ = 0;
    619 
    620  gc::PretenuringNursery pretenuringNursery;
    621 
    622  mozilla::TimeDuration timeInChunkAlloc_;
    623 
    624  // Report minor collections taking at least this long, if enabled.
    625  bool enableProfiling_ = false;
    626  bool profileWorkers_ = false;
    627 
    628  mozilla::TimeDuration profileThreshold_;
    629 
    630  // Whether to use semispace collection.
    631  bool semispaceEnabled_;
    632 
    633  // Whether we will nursery-allocate strings.
    634  bool canAllocateStrings_;
    635 
    636  // Whether we will nursery-allocate BigInts.
    637  bool canAllocateBigInts_;
    638 
    639  // Report how many strings were deduplicated.
    640  bool reportDeduplications_;
    641 
    642 #ifdef JS_GC_ZEAL
    643  // Report on the kinds of things promoted.
    644  bool reportPromotion_;
    645 #endif
    646 
    647  // Whether to report information on pretenuring, and if so the allocation
    648  // threshold at which to report details of each allocation site.
    649  gc::AllocSiteFilter pretenuringReportFilter_;
    650 
    651  // Whether and why a collection of this nursery has been requested. When this
    652  // happens |prevPosition_| is set to the current position and |position_| set
    653  // to the end of the chunk to force the next allocation to fail.
    654  JS::GCReason minorGCTriggerReason_;
    655  uintptr_t prevPosition_;
    656 
    657  // Profiling data.
    658 
    659  ProfileTimes startTimes_;
    660  ProfileDurations profileDurations_;
    661  ProfileDurations totalDurations_;
    662 
    663  // Data about the previous collection.
    664  struct PreviousGC {
    665    JS::GCReason reason = JS::GCReason::NO_REASON;
    666    size_t nurseryCapacity = 0;
    667    size_t nurseryCommitted = 0;
    668    size_t nurseryUsedBytes = 0;
    669    size_t nurseryUsedChunkCount = 0;
    670    size_t tenuredBytes = 0;
    671    size_t tenuredCells = 0;
    672    mozilla::TimeStamp endTime;
    673  };
    674  PreviousGC previousGC;
    675 
    676  bool hasRecentGrowthData;
    677  double smoothedTargetSize;
    678 
    679  // During a collection most hoisted slot and element buffers indicate their
    680  // new location with a forwarding pointer at the base. This does not work
    681  // for buffers whose length is less than pointer width, or when different
    682  // buffers might overlap each other. For these, an entry in the following
    683  // table is used.
    684  using ForwardedBufferMap =
    685      HashMap<void*, void*, PointerHasher<void*>, SystemAllocPolicy>;
    686  ForwardedBufferMap forwardedBuffers;
    687 
    688  // When we assign a unique id to cell in the nursery, that almost always
    689  // means that the cell will be in a hash table, and thus, held live,
    690  // automatically moving the uid from the nursery to its new home in
    691  // tenured. It is possible, if rare, for an object that acquired a uid to
    692  // be dead before the next collection, in which case we need to know to
    693  // remove it when we sweep.
    694  //
    695  // Note: we store the pointers as Cell* here, resulting in an ugly cast in
    696  //       sweep. This is because this structure is used to help implement
    697  //       stable object hashing and we have to break the cycle somehow.
    698  using CellsWithUniqueIdVector = JS::GCVector<gc::Cell*, 8, SystemAllocPolicy>;
    699  CellsWithUniqueIdVector cellsWithUid_;
    700 
    701  // Lists of map and set objects with iterators allocated in the nursery. Such
    702  // objects need to be swept after minor GC.
    703  using MapObjectVector = Vector<MapObject*, 0, SystemAllocPolicy>;
    704  MapObjectVector mapsWithNurseryIterators_;
    705  using SetObjectVector = Vector<SetObject*, 0, SystemAllocPolicy>;
    706  SetObjectVector setsWithNurseryIterators_;
    707 
    708  // List of strings with StringBuffers allocated in the nursery. References
    709  // to the buffers are dropped after minor GC. The list stores both the JS
    710  // string and the StringBuffer to simplify interaction with AtomRefs and
    711  // string deduplication.
    712  using StringAndBuffer = std::pair<JSLinearString*, mozilla::StringBuffer*>;
    713  using StringAndBufferVector =
    714      JS::GCVector<StringAndBuffer, 8, SystemAllocPolicy>;
    715  StringAndBufferVector stringBuffers_;
    716 
    717  // Like stringBuffers_, but for extensible strings for flattened ropes. This
    718  // requires a HashMap instead of a Vector because we need to remove the entry
    719  // when transferring the buffer to a new extensible string during flattening.
    720  using ExtensibleStringBuffers =
    721      HashMap<JSLinearString*, mozilla::StringBuffer*,
    722              js::PointerHasher<JSLinearString*>, js::SystemAllocPolicy>;
    723  ExtensibleStringBuffers extensibleStringBuffers_;
    724 
    725  // List of StringBuffers to release off-thread.
    726  using StringBufferVector =
    727      Vector<mozilla::StringBuffer*, 8, SystemAllocPolicy>;
    728  StringBufferVector stringBuffersToReleaseAfterMinorGC_;
    729 
    730  UniquePtr<NurserySweepTask> sweepTask;
    731 
    732  // Lists of weakmaps with nursery allocated keys or values. Such objects need
    733  // to be swept after minor GC.
    734  using WeakMapVector = Vector<WeakMapBase*, 0, SystemAllocPolicy>;
    735  WeakMapVector weakMapsWithNurseryEntries_;
    736 
    737  UniquePtr<NurseryDecommitTask> decommitTask;
    738 
    739  // Whether the previous collection tenured everything. This may be false if
    740  // semispace is in use.
    741  bool tenuredEverything;
    742 
    743  friend class gc::GCRuntime;
    744  friend class gc::TenuringTracer;
    745  friend struct NurseryChunk;
    746 };
    747 
    748 MOZ_ALWAYS_INLINE bool Nursery::isInside(const void* p) const {
    749  // TODO: Split this into separate methods.
    750  // TODO: Do we ever need to check both?
    751  return toSpace.isInside(p) || fromSpace.isInside(p);
    752 }
    753 
    754 MOZ_ALWAYS_INLINE bool Nursery::Space::isInside(const void* p) const {
    755  for (auto* chunk : chunks_) {
    756    if (uintptr_t(p) - uintptr_t(chunk) < gc::ChunkSize) {
    757      return true;
    758    }
    759  }
    760  return false;
    761 }
    762 
    763 }  // namespace js
    764 
    765 #endif  // gc_Nursery_h