Backed out changeset 1e582a0e5593 (bug 1852921) for causing build bustages
[gecko.git] / js / src / gc / GCMarker.h
bloba12b6881fbf5a769e6cc246167138ee98d9149b7
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef gc_GCMarker_h
8 #define gc_GCMarker_h
10 #include "mozilla/Maybe.h"
11 #include "mozilla/Variant.h"
13 #include "ds/OrderedHashTable.h"
14 #include "gc/Barrier.h"
15 #include "js/TracingAPI.h"
16 #include "js/TypeDecls.h"
17 #include "threading/ProtectedData.h"
19 class JSRope;
21 namespace js {
23 class GCMarker;
24 class SliceBudget;
25 class WeakMapBase;
27 static const size_t MARK_STACK_BASE_CAPACITY = 4096;
29 enum class SlotsOrElementsKind {
30 Unused = 0, // Must match SlotsOrElementsRangeTag
31 Elements,
32 FixedSlots,
33 DynamicSlots
36 namespace gc {
38 enum IncrementalProgress { NotFinished = 0, Finished };
40 class AutoSetMarkColor;
41 struct Cell;
42 class ParallelMarker;
43 class UnmarkGrayTracer;
45 struct EphemeronEdgeTableHashPolicy {
46 using Lookup = Cell*;
47 static HashNumber hash(const Lookup& v,
48 const mozilla::HashCodeScrambler& hcs) {
49 return hcs.scramble(mozilla::HashGeneric(v));
51 static bool match(Cell* const& k, const Lookup& l) { return k == l; }
52 static bool isEmpty(Cell* const& v) { return !v; }
53 static void makeEmpty(Cell** vp) { *vp = nullptr; }
56 // Ephemeron edges have two source nodes and one target, and mark the target
57 // with the minimum (least-marked) color of the sources. Currently, one of
58 // those sources will always be a WeakMapBase, so this will refer to its color
59 // at the time the edge is traced through. The other source's color will be
60 // given by the current mark color of the GCMarker.
61 struct EphemeronEdge {
62 CellColor color;
63 Cell* target;
65 EphemeronEdge(CellColor color_, Cell* cell) : color(color_), target(cell) {}
68 using EphemeronEdgeVector = Vector<EphemeronEdge, 2, js::SystemAllocPolicy>;
70 using EphemeronEdgeTable =
71 OrderedHashMap<Cell*, EphemeronEdgeVector, EphemeronEdgeTableHashPolicy,
72 js::SystemAllocPolicy>;
75 * The mark stack. Pointers in this stack are "gray" in the GC sense, but
76 * their references may be marked either black or gray (in the CC sense).
78 * When the mark stack is full, the GC does not call js::TraceChildren to mark
79 * the reachable "children" of the thing. Rather the thing is put aside and
80 * js::TraceChildren is called later when the mark stack is empty.
82 * To implement such delayed marking of the children with minimal overhead for
83 * the normal case of sufficient stack, we link arenas into a list using
84 * Arena::setNextDelayedMarkingArena(). The head of the list is stored in
85 * GCMarker::delayedMarkingList. GCMarker::delayMarkingChildren() adds arenas
86 * to the list as necessary while markAllDelayedChildren() pops the arenas from
87 * the stack until it is empty.
89 class MarkStack {
90 public:
92 * We use a common mark stack to mark GC things of different types and use
93 * the explicit tags to distinguish them when it cannot be deduced from
94 * the context of push or pop operation.
96 enum Tag {
97 SlotsOrElementsRangeTag = 0, // Must match SlotsOrElementsKind::Unused.
98 ObjectTag,
99 JitCodeTag,
100 ScriptTag,
101 TempRopeTag,
103 LastTag = TempRopeTag
106 static const uintptr_t TagMask = 7;
107 static_assert(TagMask >= uintptr_t(LastTag),
108 "The tag mask must subsume the tags.");
109 static_assert(TagMask <= gc::CellAlignMask,
110 "The tag mask must be embeddable in a Cell*.");
112 class TaggedPtr {
113 uintptr_t bits;
115 Cell* ptr() const;
117 public:
118 TaggedPtr() = default;
119 TaggedPtr(Tag tag, Cell* ptr);
120 Tag tag() const;
121 uintptr_t tagUnchecked() const;
122 template <typename T>
123 T* as() const;
125 JSObject* asRangeObject() const;
126 JSRope* asTempRope() const;
128 void assertValid() const;
131 struct SlotsOrElementsRange {
132 SlotsOrElementsRange(SlotsOrElementsKind kind, JSObject* obj, size_t start);
133 void assertValid() const;
135 SlotsOrElementsKind kind() const;
136 size_t start() const;
137 TaggedPtr ptr() const;
139 static constexpr size_t StartShift = 2;
140 static constexpr size_t KindMask = (1 << StartShift) - 1;
142 private:
143 uintptr_t startAndKind_;
144 TaggedPtr ptr_;
147 MarkStack();
148 ~MarkStack();
150 explicit MarkStack(const MarkStack& other);
151 MarkStack& operator=(const MarkStack& other);
153 MarkStack(MarkStack&& other) noexcept;
154 MarkStack& operator=(MarkStack&& other) noexcept;
156 // The unit for MarkStack::capacity() is mark stack words.
157 size_t capacity() { return stack().length(); }
159 size_t position() const { return topIndex_; }
161 [[nodiscard]] bool init();
162 [[nodiscard]] bool resetStackCapacity();
164 #ifdef JS_GC_ZEAL
165 void setMaxCapacity(size_t maxCapacity);
166 #endif
168 template <typename T>
169 [[nodiscard]] bool push(T* ptr);
171 [[nodiscard]] bool push(JSObject* obj, SlotsOrElementsKind kind,
172 size_t start);
173 [[nodiscard]] bool push(const TaggedPtr& ptr);
174 [[nodiscard]] bool push(const SlotsOrElementsRange& array);
175 void infalliblePush(const TaggedPtr& ptr);
176 void infalliblePush(const SlotsOrElementsRange& array);
178 // GCMarker::eagerlyMarkChildren uses unused marking stack as temporary
179 // storage to hold rope pointers.
180 [[nodiscard]] bool pushTempRope(JSRope* rope);
182 bool isEmpty() const { return position() == 0; }
183 bool hasEntries() const { return !isEmpty(); }
185 Tag peekTag() const;
186 TaggedPtr popPtr();
187 SlotsOrElementsRange popSlotsOrElementsRange();
189 void clearAndResetCapacity();
190 void clearAndFreeStack();
192 void poisonUnused();
194 [[nodiscard]] bool ensureSpace(size_t count);
196 static void moveWork(MarkStack& dst, MarkStack& src);
198 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
200 private:
201 using StackVector = Vector<TaggedPtr, 0, SystemAllocPolicy>;
202 const StackVector& stack() const { return stack_.ref(); }
203 StackVector& stack() { return stack_.ref(); }
205 /* Grow the stack, ensuring there is space for at least count elements. */
206 [[nodiscard]] bool enlarge(size_t count);
208 [[nodiscard]] bool resize(size_t newCapacity);
210 TaggedPtr* topPtr();
212 const TaggedPtr& peekPtr() const;
213 [[nodiscard]] bool pushTaggedPtr(Tag tag, Cell* ptr);
215 bool indexIsEntryBase(size_t index) const;
217 // Vector containing allocated stack memory. Unused beyond topIndex_.
218 MainThreadOrGCTaskData<StackVector> stack_;
220 // Index of the top of the stack.
221 MainThreadOrGCTaskData<size_t> topIndex_;
223 #ifdef JS_GC_ZEAL
224 // The maximum stack capacity to grow to.
225 MainThreadOrGCTaskData<size_t> maxCapacity_{SIZE_MAX};
226 #endif
229 static_assert(unsigned(SlotsOrElementsKind::Unused) ==
230 unsigned(MarkStack::SlotsOrElementsRangeTag),
231 "To split the mark stack we depend on being able to tell the "
232 "difference between SlotsOrElementsRange::startAndKind_ and a "
233 "tagged SlotsOrElementsRange");
235 // Bitmask of options to parameterize MarkingTracerT.
236 namespace MarkingOptions {
237 enum : uint32_t {
238 None = 0,
240 // Set the compartment's hasMarkedCells flag for roots.
241 MarkRootCompartments = 1,
243 // The marking tracer is operating in parallel. Use appropriate atomic
244 // accesses to update the mark bits correctly.
245 ParallelMarking = 2,
247 // Mark any implicit edges if we are in weak marking mode.
248 MarkImplicitEdges = 4,
250 } // namespace MarkingOptions
252 // A default set of marking options that works during normal marking and weak
253 // marking modes. Used for barriers and testing code.
254 constexpr uint32_t NormalMarkingOptions = MarkingOptions::MarkImplicitEdges;
256 template <uint32_t markingOptions>
257 class MarkingTracerT
258 : public GenericTracerImpl<MarkingTracerT<markingOptions>> {
259 public:
260 MarkingTracerT(JSRuntime* runtime, GCMarker* marker);
261 virtual ~MarkingTracerT() = default;
263 template <typename T>
264 void onEdge(T** thingp, const char* name);
265 friend class GenericTracerImpl<MarkingTracerT<markingOptions>>;
267 GCMarker* getMarker();
270 using MarkingTracer = MarkingTracerT<MarkingOptions::None>;
271 using RootMarkingTracer = MarkingTracerT<MarkingOptions::MarkRootCompartments>;
272 using WeakMarkingTracer = MarkingTracerT<MarkingOptions::MarkImplicitEdges>;
273 using ParallelMarkingTracer = MarkingTracerT<MarkingOptions::ParallelMarking>;
275 enum ShouldReportMarkTime : bool {
276 ReportMarkTime = true,
277 DontReportMarkTime = false
280 } /* namespace gc */
282 class GCMarker {
283 enum MarkingState : uint8_t {
284 // Have not yet started marking.
285 NotActive,
287 // Root marking mode. This sets the hasMarkedCells flag on compartments
288 // containing objects and scripts, which is used to make sure we clean up
289 // dead compartments.
290 RootMarking,
292 // Main marking mode. Weakmap marking will be populating the
293 // gcEphemeronEdges tables but not consulting them. The state will
294 // transition to WeakMarking until it is done, then back to RegularMarking.
295 RegularMarking,
297 // Like RegularMarking but with multiple threads running in parallel.
298 ParallelMarking,
300 // Same as RegularMarking except now every marked obj/script is immediately
301 // looked up in the gcEphemeronEdges table to find edges generated by
302 // weakmap keys, and traversing them to their values. Transitions back to
303 // RegularMarking when done.
304 WeakMarking,
307 public:
308 explicit GCMarker(JSRuntime* rt);
309 [[nodiscard]] bool init();
311 JSRuntime* runtime() { return runtime_; }
312 JSTracer* tracer() {
313 return tracer_.match([](auto& t) -> JSTracer* { return &t; });
316 #ifdef JS_GC_ZEAL
317 void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
318 #endif
320 bool isActive() const { return state != NotActive; }
321 bool isRegularMarking() const { return state == RegularMarking; }
322 bool isParallelMarking() const { return state == ParallelMarking; }
323 bool isWeakMarking() const { return state == WeakMarking; }
325 gc::MarkColor markColor() const { return markColor_; }
327 bool isDrained() const { return stack.isEmpty() && otherStack.isEmpty(); }
329 bool hasEntriesForCurrentColor() { return stack.hasEntries(); }
330 bool hasBlackEntries() const { return hasEntries(gc::MarkColor::Black); }
331 bool hasGrayEntries() const { return hasEntries(gc::MarkColor::Gray); }
332 bool hasEntries(gc::MarkColor color) const;
334 bool canDonateWork() const;
336 void start();
337 void stop();
338 void reset();
340 [[nodiscard]] bool markUntilBudgetExhausted(
341 SliceBudget& budget,
342 gc::ShouldReportMarkTime reportTime = gc::ReportMarkTime);
344 void setRootMarkingMode(bool newState);
346 bool enterWeakMarkingMode();
347 void leaveWeakMarkingMode();
349 void enterParallelMarkingMode(gc::ParallelMarker* pm);
350 void leaveParallelMarkingMode();
352 // Do not use linear-time weak marking for the rest of this collection.
353 // Currently, this will only be triggered by an OOM when updating needed data
354 // structures.
355 void abortLinearWeakMarking();
357 // 'delegate' is no longer the delegate of 'key'.
358 void severWeakDelegate(JSObject* key, JSObject* delegate);
360 // 'delegate' is now the delegate of 'key'. Update weakmap marking state.
361 void restoreWeakDelegate(JSObject* key, JSObject* delegate);
363 #ifdef DEBUG
364 // We can't check atom marking if the helper thread lock is already held by
365 // the current thread. This allows us to disable the check.
366 void setCheckAtomMarking(bool check);
368 bool shouldCheckCompartments() { return strictCompartmentChecking; }
369 #endif
371 bool markCurrentColorInParallel(SliceBudget& budget);
373 template <uint32_t markingOptions, gc::MarkColor>
374 bool markOneColor(SliceBudget& budget);
376 static void moveWork(GCMarker* dst, GCMarker* src);
378 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
380 static GCMarker* fromTracer(JSTracer* trc) {
381 MOZ_ASSERT(trc->isMarkingTracer());
382 auto* marker = reinterpret_cast<GCMarker*>(uintptr_t(trc) -
383 offsetof(GCMarker, tracer_));
384 MOZ_ASSERT(marker->tracer() == trc);
385 return marker;
388 // Internal public methods, for ease of use by the rest of the GC:
390 // If |thing| is unmarked, mark it and then traverse its children.
391 template <uint32_t, typename T>
392 void markAndTraverse(T* thing);
394 template <typename T>
395 void markImplicitEdges(T* oldThing);
397 private:
399 * Care must be taken changing the mark color from gray to black. The cycle
400 * collector depends on the invariant that there are no black to gray edges
401 * in the GC heap. This invariant lets the CC not trace through black
402 * objects. If this invariant is violated, the cycle collector may free
403 * objects that are still reachable.
405 void setMarkColor(gc::MarkColor newColor);
406 friend class js::gc::AutoSetMarkColor;
408 template <typename Tracer>
409 void setMarkingStateAndTracer(MarkingState prev, MarkingState next);
411 template <uint32_t markingOptions>
412 bool processMarkStackTop(SliceBudget& budget);
413 friend class gc::GCRuntime;
415 // Helper methods that coerce their second argument to the base pointer
416 // type.
417 template <uint32_t markingOptions, typename S>
418 void markAndTraverseObjectEdge(S source, JSObject* target) {
419 markAndTraverseEdge<markingOptions>(source, target);
421 template <uint32_t markingOptions, typename S>
422 void markAndTraverseStringEdge(S source, JSString* target) {
423 markAndTraverseEdge<markingOptions>(source, target);
426 template <uint32_t markingOptions, typename S, typename T>
427 void markAndTraverseEdge(S source, T* target);
428 template <uint32_t markingOptions, typename S, typename T>
429 void markAndTraverseEdge(S source, const T& target);
431 template <typename S, typename T>
432 void checkTraversedEdge(S source, T* target);
434 // Mark the given GC thing, but do not trace its children. Return true
435 // if the thing became marked.
436 template <uint32_t markingOptions, typename T>
437 [[nodiscard]] bool mark(T* thing);
439 // Traverse a GC thing's children, using a strategy depending on the type.
440 // This can either processing them immediately or push them onto the mark
441 // stack for later.
442 #define DEFINE_TRAVERSE_METHOD(_1, Type, _2, _3) \
443 template <uint32_t> \
444 void traverse(Type* thing);
445 JS_FOR_EACH_TRACEKIND(DEFINE_TRAVERSE_METHOD)
446 #undef DEFINE_TRAVERSE_METHOD
448 // Process a marked thing's children by calling T::traceChildren().
449 template <uint32_t markingOptions, typename T>
450 void traceChildren(T* thing);
452 // Process a marked thing's children recursively using an iterative loop and
453 // manual dispatch, for kinds where this is possible.
454 template <uint32_t markingOptions, typename T>
455 void scanChildren(T* thing);
457 // Push a marked thing onto the mark stack. Its children will be marked later.
458 template <uint32_t markingOptions, typename T>
459 void pushThing(T* thing);
461 template <uint32_t markingOptions>
462 void eagerlyMarkChildren(JSLinearString* str);
463 template <uint32_t markingOptions>
464 void eagerlyMarkChildren(JSRope* rope);
465 template <uint32_t markingOptions>
466 void eagerlyMarkChildren(JSString* str);
467 template <uint32_t markingOptions>
468 void eagerlyMarkChildren(Shape* shape);
469 template <uint32_t markingOptions>
470 void eagerlyMarkChildren(PropMap* map);
471 template <uint32_t markingOptions>
472 void eagerlyMarkChildren(Scope* scope);
474 template <typename T>
475 inline void pushTaggedPtr(T* ptr);
477 inline void pushValueRange(JSObject* obj, SlotsOrElementsKind kind,
478 size_t start, size_t end);
480 // Push an object onto the stack for later tracing and assert that it has
481 // already been marked.
482 inline void repush(JSObject* obj);
484 template <typename T>
485 void markImplicitEdgesHelper(T markedThing);
487 // Mark through edges whose target color depends on the colors of two source
488 // entities (eg a WeakMap and one of its keys), and push the target onto the
489 // mark stack.
490 void markEphemeronEdges(gc::EphemeronEdgeVector& edges,
491 gc::CellColor srcColor);
492 friend class JS::Zone;
494 #ifdef DEBUG
495 void checkZone(void* p);
496 #else
497 void checkZone(void* p) {}
498 #endif
500 template <uint32_t markingOptions>
501 bool doMarking(SliceBudget& budget, gc::ShouldReportMarkTime reportTime);
503 void delayMarkingChildrenOnOOM(gc::Cell* cell);
506 * The JSTracer used for marking. This can change depending on the current
507 * state.
509 mozilla::Variant<gc::MarkingTracer, gc::RootMarkingTracer,
510 gc::WeakMarkingTracer, gc::ParallelMarkingTracer>
511 tracer_;
513 JSRuntime* const runtime_;
515 // The main mark stack, holding entries of color |markColor_|.
516 gc::MarkStack stack;
518 // The auxiliary mark stack, which may contain entries of the other color.
519 gc::MarkStack otherStack;
521 // Track whether we're using the main or auxiliary stack.
522 MainThreadOrGCTaskData<bool> haveSwappedStacks;
524 // The current mark stack color.
525 MainThreadOrGCTaskData<gc::MarkColor> markColor_;
527 MainThreadOrGCTaskData<gc::ParallelMarker*> parallelMarker_;
529 Vector<JS::GCCellPtr, 0, SystemAllocPolicy> unmarkGrayStack;
530 friend class gc::UnmarkGrayTracer;
532 /* Track the state of marking. */
533 MainThreadOrGCTaskData<MarkingState> state;
535 /* Whether we successfully added all edges to the implicit edges table. */
536 MainThreadOrGCTaskData<bool> haveAllImplicitEdges;
538 public:
540 * Whether weakmaps can be marked incrementally.
542 * JSGC_INCREMENTAL_WEAKMAP_ENABLED
543 * pref: javascript.options.mem.incremental_weakmap
545 MainThreadOrGCTaskData<bool> incrementalWeakMapMarkingEnabled;
547 #ifdef DEBUG
548 private:
549 /* Assert that start and stop are called with correct ordering. */
550 MainThreadOrGCTaskData<bool> started;
553 * Whether to check that atoms traversed are present in atom marking
554 * bitmap.
556 MainThreadOrGCTaskData<bool> checkAtomMarking;
559 * If this is true, all marked objects must belong to a compartment being
560 * GCed. This is used to look for compartment bugs.
562 MainThreadOrGCTaskData<bool> strictCompartmentChecking;
564 public:
566 * The compartment and zone of the object whose trace hook is currently being
567 * called, if any. Used to catch cross-compartment edges traced without use of
568 * TraceCrossCompartmentEdge.
570 MainThreadOrGCTaskData<Compartment*> tracingCompartment;
571 MainThreadOrGCTaskData<Zone*> tracingZone;
572 #endif // DEBUG
575 namespace gc {
578 * Temporarily change the mark color while this class is on the stack.
580 * During incremental sweeping this also transitions zones in the
581 * current sweep group into the Mark or MarkGray state as appropriate.
583 class MOZ_RAII AutoSetMarkColor {
584 GCMarker& marker_;
585 MarkColor initialColor_;
587 public:
588 AutoSetMarkColor(GCMarker& marker, MarkColor newColor)
589 : marker_(marker), initialColor_(marker.markColor()) {
590 marker_.setMarkColor(newColor);
593 AutoSetMarkColor(GCMarker& marker, CellColor newColor)
594 : AutoSetMarkColor(marker, newColor.asMarkColor()) {}
596 ~AutoSetMarkColor() { marker_.setMarkColor(initialColor_); }
599 } /* namespace gc */
601 } /* namespace js */
603 #endif /* gc_GCMarker_h */