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/ArrayUtils.h"
12 #include "mozilla/EnumeratedArray.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/TimeStamp.h"
15 
16 #include "gc/GCParallelTask.h"
17 #include "gc/Heap.h"
18 #include "js/AllocPolicy.h"
19 #include "js/Class.h"
20 #include "js/GCAPI.h"
21 #include "js/HeapAPI.h"
22 #include "js/TracingAPI.h"
23 #include "js/TypeDecls.h"
24 #include "js/Vector.h"
25 #include "util/Text.h"
26 
27 #define FOR_EACH_NURSERY_PROFILE_TIME(_)      \
28   /* Key                       Header text */ \
29   _(Total, "total")                           \
30   _(TraceValues, "mkVals")                    \
31   _(TraceCells, "mkClls")                     \
32   _(TraceSlots, "mkSlts")                     \
33   _(TraceWholeCells, "mcWCll")                \
34   _(TraceGenericEntries, "mkGnrc")            \
35   _(CheckHashTables, "ckTbls")                \
36   _(MarkRuntime, "mkRntm")                    \
37   _(MarkDebugger, "mkDbgr")                   \
38   _(SweepCaches, "swpCch")                    \
39   _(CollectToObjFP, "colObj")                 \
40   _(CollectToStrFP, "colStr")                 \
41   _(ObjectsTenuredCallback, "tenCB")          \
42   _(Sweep, "sweep")                           \
43   _(UpdateJitActivations, "updtIn")           \
44   _(FreeMallocedBuffers, "frSlts")            \
45   _(ClearStoreBuffer, "clrSB")                \
46   _(ClearNursery, "clear")                    \
47   _(PurgeStringToAtomCache, "pStoA")          \
48   _(Pretenure, "pretnr")
49 
50 template <typename T>
51 class SharedMem;
52 class JSDependentString;
53 
54 namespace js {
55 
56 struct StringStats;
57 class AutoLockGCBgAlloc;
58 class ObjectElements;
59 class PlainObject;
60 class NativeObject;
61 class Nursery;
62 struct NurseryChunk;
63 class HeapSlot;
64 class JSONPrinter;
65 class MapObject;
66 class SetObject;
67 class TenuringTracer;
68 
69 namespace gc {
70 class AutoGCSession;
71 class AutoMaybeStartBackgroundAllocation;
72 class AutoTraceSession;
73 struct Cell;
74 class GCSchedulingTunables;
75 class MinorCollectionTracer;
76 class RelocationOverlay;
77 class StringRelocationOverlay;
78 enum class AllocKind : uint8_t;
79 class TenuredCell;
80 }  // namespace gc
81 
82 namespace jit {
83 class MacroAssembler;
84 }  // namespace jit
85 
86 class NurseryDecommitTask : public GCParallelTask {
87  public:
88   explicit NurseryDecommitTask(gc::GCRuntime* gc);
89   bool reserveSpaceForBytes(size_t nbytes);
90 
91   bool isEmpty(const AutoLockHelperThreadState& lock) const;
92 
93   void queueChunk(NurseryChunk* chunk, const AutoLockHelperThreadState& lock);
94   void queueRange(size_t newCapacity, NurseryChunk& chunk,
95                   const AutoLockHelperThreadState& lock);
96 
97  private:
98   using NurseryChunkVector = Vector<NurseryChunk*, 0, SystemAllocPolicy>;
99 
100   void run(AutoLockHelperThreadState& lock) override;
101 
chunksToDecommit()102   NurseryChunkVector& chunksToDecommit() { return chunksToDecommit_.ref(); }
chunksToDecommit()103   const NurseryChunkVector& chunksToDecommit() const {
104     return chunksToDecommit_.ref();
105   }
106 
107   MainThreadOrGCTaskData<NurseryChunkVector> chunksToDecommit_;
108 
109   MainThreadOrGCTaskData<NurseryChunk*> partialChunk;
110   MainThreadOrGCTaskData<size_t> partialCapacity;
111 };
112 
113 // Classes with JSCLASS_SKIP_NURSERY_FINALIZE or Wrapper classes with
114 // CROSS_COMPARTMENT flags will not have their finalizer called if they are
115 // nursery allocated and not promoted to the tenured heap. The finalizers for
116 // these classes must do nothing except free data which was allocated via
117 // Nursery::allocateBuffer.
CanNurseryAllocateFinalizedClass(const JSClass * const clasp)118 inline bool CanNurseryAllocateFinalizedClass(const JSClass* const clasp) {
119   MOZ_ASSERT(clasp->hasFinalize());
120   return clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE;
121 }
122 
123 class Nursery {
124  public:
125   static const size_t Alignment = gc::ChunkSize;
126   static const size_t ChunkShift = gc::ChunkShift;
127 
128   using BufferRelocationOverlay = void*;
129   using BufferSet = HashSet<void*, PointerHasher<void*>, SystemAllocPolicy>;
130 
131   explicit Nursery(gc::GCRuntime* gc);
132   ~Nursery();
133 
134   [[nodiscard]] bool init(AutoLockGCBgAlloc& lock);
135 
136   // Number of allocated (ready to use) chunks.
allocatedChunkCount()137   unsigned allocatedChunkCount() const { return chunks_.length(); }
138 
139   // Total number of chunks and the capacity of the nursery. Chunks will be
140   // lazilly allocated and added to the chunks array up to this limit, after
141   // that the nursery must be collected, this limit may be raised during
142   // collection.
maxChunkCount()143   unsigned maxChunkCount() const {
144     MOZ_ASSERT(capacity());
145     return HowMany(capacity(), gc::ChunkSize);
146   }
147 
148   void enable();
149   void disable();
isEnabled()150   bool isEnabled() const { return capacity() != 0; }
151 
152   void enableStrings();
153   void disableStrings();
canAllocateStrings()154   bool canAllocateStrings() const { return canAllocateStrings_; }
155 
156   void enableBigInts();
157   void disableBigInts();
canAllocateBigInts()158   bool canAllocateBigInts() const { return canAllocateBigInts_; }
159 
160   // Return true if no allocations have been made since the last collection.
161   bool isEmpty() const;
162 
163   // Check whether an arbitrary pointer is within the nursery. This is
164   // slower than IsInsideNursery(Cell*), but works on all types of pointers.
165   MOZ_ALWAYS_INLINE bool isInside(gc::Cell* cellp) const = delete;
isInside(const void * p)166   MOZ_ALWAYS_INLINE bool isInside(const void* p) const {
167     for (auto chunk : chunks_) {
168       if (uintptr_t(p) - uintptr_t(chunk) < gc::ChunkSize) {
169         return true;
170       }
171     }
172     return false;
173   }
174 
175   template <typename T>
176   inline bool isInside(const SharedMem<T>& p) const;
177 
178   // Allocate and return a pointer to a new GC object with its |slots|
179   // pointer pre-filled. Returns nullptr if the Nursery is full.
180   JSObject* allocateObject(gc::AllocSite* site, size_t size,
181                            size_t numDynamicSlots, const JSClass* clasp);
182 
183   // Allocate and return a pointer to a new GC thing. Returns nullptr if the
184   // Nursery is full.
185   gc::Cell* allocateCell(gc::AllocSite* site, size_t size, JS::TraceKind kind);
186 
allocateBigInt(gc::AllocSite * site,size_t size)187   gc::Cell* allocateBigInt(gc::AllocSite* site, size_t size) {
188     return allocateCell(site, size, JS::TraceKind::BigInt);
189   }
190   gc::Cell* allocateString(gc::AllocSite* site, size_t size);
191 
nurseryCellHeaderSize()192   static size_t nurseryCellHeaderSize() {
193     return sizeof(gc::NurseryCellHeader);
194   }
195 
196   // Allocate a buffer for a given zone, using the nursery if possible.
197   void* allocateBuffer(JS::Zone* zone, size_t nbytes);
198 
199   // Allocate a buffer for a given object, using the nursery if possible and
200   // obj is in the nursery.
201   void* allocateBuffer(JSObject* obj, size_t nbytes);
202 
203   // Allocate a buffer for a given object, always using the nursery if obj is
204   // in the nursery. The requested size must be less than or equal to
205   // MaxNurseryBufferSize.
206   void* allocateBufferSameLocation(JSObject* obj, size_t nbytes);
207 
208   // Allocate a zero-initialized buffer for a given zone, using the nursery if
209   // possible. If the buffer isn't allocated in the nursery, the given arena is
210   // used.
211   void* allocateZeroedBuffer(JS::Zone* zone, size_t nbytes,
212                              arena_id_t arena = js::MallocArena);
213 
214   // Allocate a zero-initialized buffer for a given object, using the nursery if
215   // possible and obj is in the nursery. If the buffer isn't allocated in the
216   // nursery, the given arena is used.
217   void* allocateZeroedBuffer(JSObject* obj, size_t nbytes,
218                              arena_id_t arena = js::MallocArena);
219 
220   // Resize an existing buffer.
221   void* reallocateBuffer(JS::Zone* zone, gc::Cell* cell, void* oldBuffer,
222                          size_t oldBytes, size_t newBytes);
223 
224   // Allocate a digits buffer for a given BigInt, using the nursery if possible
225   // and |bi| is in the nursery.
226   void* allocateBuffer(JS::BigInt* bi, size_t nbytes);
227 
228   // Free an object buffer.
229   void freeBuffer(void* buffer, size_t nbytes);
230 
231   // The maximum number of bytes allowed to reside in nursery buffers.
232   static const size_t MaxNurseryBufferSize = 1024;
233 
234   // Do a minor collection.
235   void collect(JS::GCOptions options, JS::GCReason reason);
236 
237   // If the thing at |*ref| in the Nursery has been forwarded, set |*ref| to
238   // the new location and return true. Otherwise return false and leave
239   // |*ref| unset.
240   [[nodiscard]] MOZ_ALWAYS_INLINE static bool getForwardedPointer(
241       js::gc::Cell** ref);
242 
243   // Forward a slots/elements pointer stored in an Ion frame.
244   void forwardBufferPointer(uintptr_t* pSlotsElems);
245 
246   inline void maybeSetForwardingPointer(JSTracer* trc, void* oldData,
247                                         void* newData, bool direct);
248   inline void setForwardingPointerWhileTenuring(void* oldData, void* newData,
249                                                 bool direct);
250 
251   // Register a malloced buffer that is held by a nursery object, which
252   // should be freed at the end of a minor GC. Buffers are unregistered when
253   // their owning objects are tenured.
254   [[nodiscard]] bool registerMallocedBuffer(void* buffer, size_t nbytes);
255 
256   // Mark a malloced buffer as no longer needing to be freed.
removeMallocedBuffer(void * buffer,size_t nbytes)257   void removeMallocedBuffer(void* buffer, size_t nbytes) {
258     MOZ_ASSERT(mallocedBuffers.has(buffer));
259     MOZ_ASSERT(nbytes > 0);
260     MOZ_ASSERT(mallocedBufferBytes >= nbytes);
261     mallocedBuffers.remove(buffer);
262     mallocedBufferBytes -= nbytes;
263   }
264 
265   // Mark a malloced buffer as no longer needing to be freed during minor
266   // GC. There's no need to account for the size here since all remaining
267   // buffers will soon be freed.
removeMallocedBufferDuringMinorGC(void * buffer)268   void removeMallocedBufferDuringMinorGC(void* buffer) {
269     MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
270     MOZ_ASSERT(mallocedBuffers.has(buffer));
271     mallocedBuffers.remove(buffer);
272   }
273 
addedUniqueIdToCell(gc::Cell * cell)274   [[nodiscard]] bool addedUniqueIdToCell(gc::Cell* cell) {
275     MOZ_ASSERT(IsInsideNursery(cell));
276     MOZ_ASSERT(isEnabled());
277     return cellsWithUid_.append(cell);
278   }
279 
sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf)280   size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const {
281     size_t total = 0;
282     for (BufferSet::Range r = mallocedBuffers.all(); !r.empty(); r.popFront()) {
283       total += mallocSizeOf(r.front());
284     }
285     total += mallocedBuffers.shallowSizeOfExcludingThis(mallocSizeOf);
286     return total;
287   }
288 
289   // The number of bytes from the start position to the end of the nursery.
290   // pass maxChunkCount(), allocatedChunkCount() or chunkCountLimit()
291   // to calculate the nursery size, current lazy-allocated size or nursery
292   // limit respectively.
293   size_t spaceToEnd(unsigned chunkCount) const;
294 
capacity()295   size_t capacity() const { return capacity_; }
committed()296   size_t committed() const { return spaceToEnd(allocatedChunkCount()); }
297 
298   // Used and free space both include chunk headers for that part of the
299   // nursery.
300   //
301   // usedSpace() + freeSpace() == capacity()
302   //
usedSpace()303   MOZ_ALWAYS_INLINE size_t usedSpace() const {
304     return capacity() - freeSpace();
305   }
freeSpace()306   MOZ_ALWAYS_INLINE size_t freeSpace() const {
307     MOZ_ASSERT(isEnabled());
308     MOZ_ASSERT(currentEnd_ - position_ <= NurseryChunkUsableSize);
309     MOZ_ASSERT(currentChunk_ < maxChunkCount());
310     return (currentEnd_ - position_) +
311            (maxChunkCount() - currentChunk_ - 1) * gc::ChunkSize;
312   }
313 
314 #ifdef JS_GC_ZEAL
315   void enterZealMode();
316   void leaveZealMode();
317 #endif
318 
319   // Write profile time JSON on JSONPrinter.
320   void renderProfileJSON(JSONPrinter& json) const;
321 
322   // Print header line for profile times.
323   void printProfileHeader();
324 
325   // Print total profile times on shutdown.
326   void printTotalProfileTimes();
327 
addressOfPosition()328   void* addressOfPosition() const { return (void**)&position_; }
addressOfCurrentEnd()329   const void* addressOfCurrentEnd() const { return (void**)&currentEnd_; }
addressOfCurrentStringEnd()330   const void* addressOfCurrentStringEnd() const {
331     return (void*)&currentStringEnd_;
332   }
addressOfCurrentBigIntEnd()333   const void* addressOfCurrentBigIntEnd() const {
334     return (void*)&currentBigIntEnd_;
335   }
addressOfNurseryAllocatedSites()336   void* addressOfNurseryAllocatedSites() {
337     return pretenuringNursery.addressOfAllocatedSites();
338   }
339 
340   void requestMinorGC(JS::GCReason reason) const;
341 
minorGCRequested()342   bool minorGCRequested() const {
343     return minorGCTriggerReason_ != JS::GCReason::NO_REASON;
344   }
minorGCTriggerReason()345   JS::GCReason minorGCTriggerReason() const { return minorGCTriggerReason_; }
clearMinorGCRequest()346   void clearMinorGCRequest() {
347     minorGCTriggerReason_ = JS::GCReason::NO_REASON;
348   }
349 
350   bool shouldCollect() const;
351   bool isNearlyFull() const;
352   bool isUnderused() const;
353 
enableProfiling()354   bool enableProfiling() const { return enableProfiling_; }
355 
addMapWithNurseryMemory(MapObject * obj)356   bool addMapWithNurseryMemory(MapObject* obj) {
357     MOZ_ASSERT_IF(!mapsWithNurseryMemory_.empty(),
358                   mapsWithNurseryMemory_.back() != obj);
359     return mapsWithNurseryMemory_.append(obj);
360   }
addSetWithNurseryMemory(SetObject * obj)361   bool addSetWithNurseryMemory(SetObject* obj) {
362     MOZ_ASSERT_IF(!setsWithNurseryMemory_.empty(),
363                   setsWithNurseryMemory_.back() != obj);
364     return setsWithNurseryMemory_.append(obj);
365   }
366 
367   // The amount of space in the mapped nursery available to allocations.
368   static const size_t NurseryChunkUsableSize =
369       gc::ChunkSize - sizeof(gc::ChunkBase);
370 
joinDecommitTask()371   void joinDecommitTask() { decommitTask.join(); }
372 
collectionStartTime()373   mozilla::TimeStamp collectionStartTime() {
374     return startTimes_[ProfileKey::Total];
375   }
376 
canCreateAllocSite()377   bool canCreateAllocSite() { return pretenuringNursery.canCreateAllocSite(); }
noteAllocSiteCreated()378   void noteAllocSiteCreated() { pretenuringNursery.noteAllocSiteCreated(); }
reportPretenuring()379   bool reportPretenuring() const { return reportPretenuring_; }
maybeStopPretenuring(gc::GCRuntime * gc)380   void maybeStopPretenuring(gc::GCRuntime* gc) {
381     pretenuringNursery.maybeStopPretenuring(gc);
382   }
383 
384   // Round a size in bytes to the nearest valid nursery size.
385   static size_t roundSize(size_t size);
386 
387  private:
388   gc::GCRuntime* const gc;
389 
390   // Vector of allocated chunks to allocate from.
391   Vector<NurseryChunk*, 0, SystemAllocPolicy> chunks_;
392 
393   // Pointer to the first unallocated byte in the nursery.
394   uintptr_t position_;
395 
396   // These fields refer to the beginning of the nursery. They're normally 0
397   // and chunk(0).start() respectively. Except when a generational GC zeal
398   // mode is active, then they may be arbitrary (see Nursery::clear()).
399   unsigned currentStartChunk_;
400   uintptr_t currentStartPosition_;
401 
402   // Pointer to the last byte of space in the current chunk.
403   uintptr_t currentEnd_;
404 
405   // Pointer to the last byte of space in the current chunk, or nullptr if we
406   // are not allocating strings in the nursery.
407   uintptr_t currentStringEnd_;
408 
409   // Pointer to the last byte of space in the current chunk, or nullptr if we
410   // are not allocating BigInts in the nursery.
411   uintptr_t currentBigIntEnd_;
412 
413   // The index of the chunk that is currently being allocated from.
414   unsigned currentChunk_;
415 
416   // The current nursery capacity measured in bytes. It may grow up to this
417   // value without a collection, allocating chunks on demand. This limit may be
418   // changed by maybeResizeNursery() each collection. It includes chunk headers.
419   size_t capacity_;
420 
421   gc::PretenuringNursery pretenuringNursery;
422 
423   mozilla::TimeDuration timeInChunkAlloc_;
424 
425   // Report minor collections taking at least this long, if enabled.
426   bool enableProfiling_;
427   bool profileWorkers_;
428   mozilla::TimeDuration profileThreshold_;
429 
430   // Whether we will nursery-allocate strings.
431   bool canAllocateStrings_;
432 
433   // Whether we will nursery-allocate BigInts.
434   bool canAllocateBigInts_;
435 
436   // Report how many strings were deduplicated.
437   bool reportDeduplications_;
438 
439   // Whether to report information on pretenuring, and if so the allocation
440   // threshold at which to report details of each allocation site.
441   bool reportPretenuring_;
442   size_t reportPretenuringThreshold_;
443 
444   // Whether and why a collection of this nursery has been requested. This is
445   // mutable as it is set by the store buffer, which otherwise cannot modify
446   // anything in the nursery.
447   mutable JS::GCReason minorGCTriggerReason_;
448 
449   // Profiling data.
450 
451   enum class ProfileKey {
452 #define DEFINE_TIME_KEY(name, text) name,
453     FOR_EACH_NURSERY_PROFILE_TIME(DEFINE_TIME_KEY)
454 #undef DEFINE_TIME_KEY
455         KeyCount
456   };
457 
458   using ProfileTimes =
459       mozilla::EnumeratedArray<ProfileKey, ProfileKey::KeyCount,
460                                mozilla::TimeStamp>;
461   using ProfileDurations =
462       mozilla::EnumeratedArray<ProfileKey, ProfileKey::KeyCount,
463                                mozilla::TimeDuration>;
464 
465   ProfileTimes startTimes_;
466   ProfileDurations profileDurations_;
467   ProfileDurations totalDurations_;
468 
469   // Data about the previous collection.
470   struct PreviousGC {
471     JS::GCReason reason = JS::GCReason::NO_REASON;
472     size_t nurseryCapacity = 0;
473     size_t nurseryCommitted = 0;
474     size_t nurseryUsedBytes = 0;
475     size_t tenuredBytes = 0;
476     size_t tenuredCells = 0;
477     mozilla::TimeStamp endTime;
478   };
479   PreviousGC previousGC;
480 
481   bool hasRecentGrowthData;
482   double smoothedTargetSize;
483 
484   // Calculate the promotion rate of the most recent minor GC.
485   // The valid_for_tenuring parameter is used to return whether this
486   // promotion rate is accurate enough (the nursery was full enough) to be
487   // used for tenuring and other decisions.
488   //
489   // Must only be called if the previousGC data is initialised.
490   double calcPromotionRate(bool* validForTenuring) const;
491 
492   // The set of externally malloced buffers potentially kept live by objects
493   // stored in the nursery. Any external buffers that do not belong to a
494   // tenured thing at the end of a minor GC must be freed.
495   BufferSet mallocedBuffers;
496   size_t mallocedBufferBytes = 0;
497 
498   // During a collection most hoisted slot and element buffers indicate their
499   // new location with a forwarding pointer at the base. This does not work
500   // for buffers whose length is less than pointer width, or when different
501   // buffers might overlap each other. For these, an entry in the following
502   // table is used.
503   typedef HashMap<void*, void*, PointerHasher<void*>, SystemAllocPolicy>
504       ForwardedBufferMap;
505   ForwardedBufferMap forwardedBuffers;
506 
507   // When we assign a unique id to cell in the nursery, that almost always
508   // means that the cell will be in a hash table, and thus, held live,
509   // automatically moving the uid from the nursery to its new home in
510   // tenured. It is possible, if rare, for an object that acquired a uid to
511   // be dead before the next collection, in which case we need to know to
512   // remove it when we sweep.
513   //
514   // Note: we store the pointers as Cell* here, resulting in an ugly cast in
515   //       sweep. This is because this structure is used to help implement
516   //       stable object hashing and we have to break the cycle somehow.
517   using CellsWithUniqueIdVector = Vector<gc::Cell*, 8, SystemAllocPolicy>;
518   CellsWithUniqueIdVector cellsWithUid_;
519 
520   template <typename Key>
521   struct DeduplicationStringHasher {
522     using Lookup = Key;
523 
hashDeduplicationStringHasher524     static inline HashNumber hash(const Lookup& lookup) {
525       JS::AutoCheckCannotGC nogc;
526       HashNumber strHash;
527 
528       // Include flags in the hash. A string relocation overlay stores either
529       // the nursery root base chars or the dependent string nursery base, but
530       // does not indicate which one. If strings with different string types
531       // were deduplicated, for example, a dependent string gets deduplicated
532       // into an extensible string, the base chain would be broken and the root
533       // base would be unreachable.
534 
535       if (lookup->asLinear().hasLatin1Chars()) {
536         strHash = mozilla::HashString(lookup->asLinear().latin1Chars(nogc),
537                                       lookup->length());
538       } else {
539         MOZ_ASSERT(lookup->asLinear().hasTwoByteChars());
540         strHash = mozilla::HashString(lookup->asLinear().twoByteChars(nogc),
541                                       lookup->length());
542       }
543 
544       return mozilla::HashGeneric(strHash, lookup->zone(), lookup->flags());
545     }
546 
matchDeduplicationStringHasher547     static MOZ_ALWAYS_INLINE bool match(const Key& key, const Lookup& lookup) {
548       if (!key->sameLengthAndFlags(*lookup) ||
549           key->asTenured().zone() != lookup->zone() ||
550           key->asTenured().getAllocKind() != lookup->getAllocKind()) {
551         return false;
552       }
553 
554       JS::AutoCheckCannotGC nogc;
555 
556       if (key->asLinear().hasLatin1Chars()) {
557         MOZ_ASSERT(lookup->asLinear().hasLatin1Chars());
558         return mozilla::ArrayEqual(key->asLinear().latin1Chars(nogc),
559                                    lookup->asLinear().latin1Chars(nogc),
560                                    lookup->length());
561       } else {
562         MOZ_ASSERT(key->asLinear().hasTwoByteChars());
563         MOZ_ASSERT(lookup->asLinear().hasTwoByteChars());
564         return EqualChars(key->asLinear().twoByteChars(nogc),
565                           lookup->asLinear().twoByteChars(nogc),
566                           lookup->length());
567       }
568     }
569   };
570 
571   using StringDeDupSet =
572       HashSet<JSString*, DeduplicationStringHasher<JSString*>,
573               SystemAllocPolicy>;
574 
575   // deDupSet is emplaced at the beginning of the nursery collection and reset
576   // at the end of the nursery collection. It can also be reset during nursery
577   // collection when out of memory to insert new entries.
578   mozilla::Maybe<StringDeDupSet> stringDeDupSet;
579 
580   // Lists of map and set objects allocated in the nursery or with iterators
581   // allocated there. Such objects need to be swept after minor GC.
582   Vector<MapObject*, 0, SystemAllocPolicy> mapsWithNurseryMemory_;
583   Vector<SetObject*, 0, SystemAllocPolicy> setsWithNurseryMemory_;
584 
585   NurseryDecommitTask decommitTask;
586 
587 #ifdef JS_GC_ZEAL
588   struct Canary;
589   Canary* lastCanary_;
590 #endif
591 
chunk(unsigned index)592   NurseryChunk& chunk(unsigned index) const { return *chunks_[index]; }
593 
594   // Set the current chunk. This updates the currentChunk_, position_
595   // currentEnd_ and currentStringEnd_ values as approprite. It'll also
596   // poison the chunk, either a portion of the chunk if it is already the
597   // current chunk, or the whole chunk if fullPoison is true or it is not
598   // the current chunk.
599   void setCurrentChunk(unsigned chunkno);
600 
601   bool initFirstChunk(AutoLockGCBgAlloc& lock);
602 
603   // extent is advisory, it will be ignored in sub-chunk and generational zeal
604   // modes. It will be clamped to Min(NurseryChunkUsableSize, capacity_).
605   void poisonAndInitCurrentChunk(size_t extent = gc::ChunkSize);
606 
607   void setCurrentEnd();
608   void setStartPosition();
609 
610   // Allocate the next chunk, or the first chunk for initialization.
611   // Callers will probably want to call setCurrentChunk(0) next.
612   [[nodiscard]] bool allocateNextChunk(unsigned chunkno,
613                                        AutoLockGCBgAlloc& lock);
614 
615   MOZ_ALWAYS_INLINE uintptr_t currentEnd() const;
616 
position()617   uintptr_t position() const { return position_; }
618 
619   MOZ_ALWAYS_INLINE bool isSubChunkMode() const;
620 
621   JSRuntime* runtime() const;
622   gcstats::Statistics& stats() const;
623 
624   const js::gc::GCSchedulingTunables& tunables() const;
625 
626   // Common internal allocator function.
627   void* allocate(size_t size);
628 
629   void* moveToNextChunkAndAllocate(size_t size);
630 
631 #ifdef JS_GC_ZEAL
632   void writeCanary(uintptr_t address);
633 #endif
634 
635   struct CollectionResult {
636     size_t tenuredBytes;
637     size_t tenuredCells;
638   };
639   CollectionResult doCollection(JS::GCReason reason);
640   void traceRoots(gc::AutoGCSession& session, TenuringTracer& mover);
641 
642   size_t doPretenuring(JSRuntime* rt, JS::GCReason reason,
643                        bool validPromotionRate, double promotionRate);
644 
645   // Handle relocation of slots/elements pointers stored in Ion frames.
646   inline void setForwardingPointer(void* oldData, void* newData, bool direct);
647 
648   inline void setDirectForwardingPointer(void* oldData, void* newData);
649   void setIndirectForwardingPointer(void* oldData, void* newData);
650 
651   inline void setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots,
652                                         uint32_t nslots);
653   inline void setElementsForwardingPointer(ObjectElements* oldHeader,
654                                            ObjectElements* newHeader,
655                                            uint32_t capacity);
656 
657 #ifdef DEBUG
658   bool checkForwardingPointerLocation(void* ptr, bool expectedInside);
659 #endif
660 
661   // Updates pointers to nursery objects that have been tenured and discards
662   // pointers to objects that have been freed.
663   void sweep();
664 
665   // Reset the current chunk and position after a minor collection. Also poison
666   // the nursery on debug & nightly builds.
667   void clear();
668 
669   void sweepMapAndSetObjects();
670 
671   // Change the allocable space provided by the nursery.
672   void maybeResizeNursery(JS::GCOptions options, JS::GCReason reason);
673   size_t targetSize(JS::GCOptions options, JS::GCReason reason);
674   void clearRecentGrowthData();
675   void growAllocableSpace(size_t newCapacity);
676   void shrinkAllocableSpace(size_t newCapacity);
677   void minimizeAllocableSpace();
678 
679   // Free the chunks starting at firstFreeChunk until the end of the chunks
680   // vector. Shrinks the vector but does not update maxChunkCount().
681   void freeChunksFrom(unsigned firstFreeChunk);
682 
683   void sendTelemetry(JS::GCReason reason, mozilla::TimeDuration totalTime,
684                      bool wasEmpty, double promotionRate,
685                      size_t sitesPretenured);
686 
687   void printCollectionProfile(JS::GCReason reason, double promotionRate);
688   void printDeduplicationData(js::StringStats& prev, js::StringStats& curr);
689 
690   // Profile recording and printing.
691   void maybeClearProfileDurations();
692   void startProfile(ProfileKey key);
693   void endProfile(ProfileKey key);
694   static void printProfileDurations(FILE* file, const ProfileDurations& times);
695 
696   mozilla::TimeStamp collectionStartTime() const;
697   mozilla::TimeStamp lastCollectionEndTime() const;
698 
699   friend class TenuringTracer;
700   friend class gc::MinorCollectionTracer;
701   friend class jit::MacroAssembler;
702   friend struct NurseryChunk;
703 };
704 
705 }  // namespace js
706 
707 #endif  // gc_Nursery_h
708