/src/mozilla-central/js/src/gc/GCMarker.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | | * vim: set ts=8 sts=4 et sw=4 tw=99: |
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/. */ |
6 | | |
7 | | #ifndef gc_GCMarker_h |
8 | | #define gc_GCMarker_h |
9 | | |
10 | | #include "ds/OrderedHashTable.h" |
11 | | #include "js/SliceBudget.h" |
12 | | #include "js/TracingAPI.h" |
13 | | #include "js/TypeDecls.h" |
14 | | |
15 | | namespace js { |
16 | | |
17 | | class AutoAccessAtomsZone; |
18 | | class WeakMapBase; |
19 | | |
20 | | static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096; |
21 | | static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768; |
22 | | |
23 | | namespace gc { |
24 | | |
25 | | struct Cell; |
26 | | |
27 | | struct WeakKeyTableHashPolicy { |
28 | | typedef JS::GCCellPtr Lookup; |
29 | 0 | static HashNumber hash(const Lookup& v, const mozilla::HashCodeScrambler&) { |
30 | 0 | return mozilla::HashGeneric(v.asCell()); |
31 | 0 | } |
32 | 0 | static bool match(const JS::GCCellPtr& k, const Lookup& l) { return k == l; } |
33 | 0 | static bool isEmpty(const JS::GCCellPtr& v) { return !v; } |
34 | 0 | static void makeEmpty(JS::GCCellPtr* vp) { *vp = nullptr; } |
35 | | }; |
36 | | |
37 | | struct WeakMarkable { |
38 | | WeakMapBase* weakmap; |
39 | | JS::GCCellPtr key; |
40 | | |
41 | | WeakMarkable(WeakMapBase* weakmapArg, JS::GCCellPtr keyArg) |
42 | 0 | : weakmap(weakmapArg), key(keyArg) {} |
43 | | }; |
44 | | |
45 | | using WeakEntryVector = Vector<WeakMarkable, 2, js::SystemAllocPolicy>; |
46 | | |
47 | | using WeakKeyTable = OrderedHashMap<JS::GCCellPtr, |
48 | | WeakEntryVector, |
49 | | WeakKeyTableHashPolicy, |
50 | | js::SystemAllocPolicy>; |
51 | | |
52 | | /* |
53 | | * When the native stack is low, the GC does not call js::TraceChildren to mark |
54 | | * the reachable "children" of the thing. Rather the thing is put aside and |
55 | | * js::TraceChildren is called later with more space on the C stack. |
56 | | * |
57 | | * To implement such delayed marking of the children with minimal overhead for |
58 | | * the normal case of sufficient native stack, the code adds a field per arena. |
59 | | * The field markingDelay->link links all arenas with delayed things into a |
60 | | * stack list with the pointer to stack top in GCMarker::unmarkedArenaStackTop. |
61 | | * GCMarker::delayMarkingChildren adds arenas to the stack as necessary while |
62 | | * markDelayedChildren pops the arenas from the stack until it empties. |
63 | | */ |
64 | | class MarkStack |
65 | | { |
66 | | public: |
67 | | /* |
68 | | * We use a common mark stack to mark GC things of different types and use |
69 | | * the explicit tags to distinguish them when it cannot be deduced from |
70 | | * the context of push or pop operation. |
71 | | */ |
72 | | enum Tag { |
73 | | ValueArrayTag, |
74 | | ObjectTag, |
75 | | GroupTag, |
76 | | SavedValueArrayTag, |
77 | | JitCodeTag, |
78 | | ScriptTag, |
79 | | TempRopeTag, |
80 | | |
81 | | LastTag = TempRopeTag |
82 | | }; |
83 | | |
84 | | static const uintptr_t TagMask = 7; |
85 | | static_assert(TagMask >= uintptr_t(LastTag), "The tag mask must subsume the tags."); |
86 | | static_assert(TagMask <= gc::CellAlignMask, "The tag mask must be embeddable in a Cell*."); |
87 | | |
88 | | class TaggedPtr |
89 | | { |
90 | | uintptr_t bits; |
91 | | |
92 | | Cell* ptr() const; |
93 | | |
94 | | public: |
95 | 12.2k | TaggedPtr() {} |
96 | | TaggedPtr(Tag tag, Cell* ptr); |
97 | | Tag tag() const; |
98 | | template <typename T> T* as() const; |
99 | | |
100 | | JSObject* asValueArrayObject() const; |
101 | | JSObject* asSavedValueArrayObject() const; |
102 | | JSRope* asTempRope() const; |
103 | | |
104 | | void assertValid() const; |
105 | | }; |
106 | | |
107 | | struct ValueArray |
108 | | { |
109 | | ValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end); |
110 | | void assertValid() const; |
111 | | |
112 | | HeapSlot* end; |
113 | | HeapSlot* start; |
114 | | TaggedPtr ptr; |
115 | | }; |
116 | | |
117 | | struct SavedValueArray |
118 | | { |
119 | | SavedValueArray(JSObject* obj, size_t index, HeapSlot::Kind kind); |
120 | | void assertValid() const; |
121 | | |
122 | | uintptr_t kind; |
123 | | uintptr_t index; |
124 | | TaggedPtr ptr; |
125 | | }; |
126 | | |
127 | | explicit MarkStack(size_t maxCapacity = DefaultCapacity); |
128 | | ~MarkStack(); |
129 | | |
130 | | static const size_t DefaultCapacity = SIZE_MAX; |
131 | | |
132 | 15.4k | size_t capacity() { return stack().length(); } |
133 | | |
134 | 18 | size_t position() const { |
135 | 18 | return topIndex_; |
136 | 18 | } |
137 | | |
138 | | MOZ_MUST_USE bool init(JSGCMode gcMode); |
139 | | |
140 | | MOZ_MUST_USE bool setCapacityForMode(JSGCMode mode); |
141 | | |
142 | 0 | size_t maxCapacity() const { return maxCapacity_; } |
143 | | void setMaxCapacity(size_t maxCapacity); |
144 | | |
145 | | template <typename T> |
146 | | MOZ_MUST_USE bool push(T* ptr); |
147 | | |
148 | | MOZ_MUST_USE bool push(JSObject* obj, HeapSlot* start, HeapSlot* end); |
149 | | MOZ_MUST_USE bool push(const ValueArray& array); |
150 | | MOZ_MUST_USE bool push(const SavedValueArray& array); |
151 | | |
152 | | // GCMarker::eagerlyMarkChildren uses unused marking stack as temporary |
153 | | // storage to hold rope pointers. |
154 | | MOZ_MUST_USE bool pushTempRope(JSRope* ptr); |
155 | | |
156 | 15.6k | bool isEmpty() const { |
157 | 15.6k | return topIndex_ == 0; |
158 | 15.6k | } |
159 | | |
160 | | Tag peekTag() const; |
161 | | TaggedPtr popPtr(); |
162 | | ValueArray popValueArray(); |
163 | | SavedValueArray popSavedValueArray(); |
164 | | |
165 | 18 | void clear() { |
166 | 18 | topIndex_ = 0; |
167 | 18 | } |
168 | | |
169 | | void setGCMode(JSGCMode gcMode); |
170 | | |
171 | | void poisonUnused(); |
172 | | |
173 | | size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; |
174 | | |
175 | | private: |
176 | | using StackVector = Vector<TaggedPtr, 0, SystemAllocPolicy>; |
177 | 25.2k | const StackVector& stack() const { return stack_.ref(); } |
178 | 47.1k | StackVector& stack() { return stack_.ref(); } |
179 | | |
180 | | MOZ_MUST_USE bool ensureSpace(size_t count); |
181 | | |
182 | | /* Grow the stack, ensuring there is space for at least count elements. */ |
183 | | MOZ_MUST_USE bool enlarge(size_t count); |
184 | | |
185 | | MOZ_MUST_USE bool resize(size_t newCapacity); |
186 | | |
187 | | TaggedPtr* topPtr(); |
188 | | |
189 | | const TaggedPtr& peekPtr() const; |
190 | | MOZ_MUST_USE bool pushTaggedPtr(Tag tag, Cell* ptr); |
191 | | |
192 | | // Index of the top of the stack. |
193 | | MainThreadData<size_t> topIndex_; |
194 | | |
195 | | // The maximum stack capacity to grow to. |
196 | | MainThreadData<size_t> maxCapacity_; |
197 | | |
198 | | // Vector containing allocated stack memory. Unused beyond topIndex_. |
199 | | MainThreadData<StackVector> stack_; |
200 | | |
201 | | #ifdef DEBUG |
202 | | mutable size_t iteratorCount_; |
203 | | #endif |
204 | | |
205 | | friend class MarkStackIter; |
206 | | }; |
207 | | |
208 | | class MarkStackIter |
209 | | { |
210 | | MarkStack& stack_; |
211 | | size_t pos_; |
212 | | |
213 | | public: |
214 | | explicit MarkStackIter(MarkStack& stack); |
215 | | ~MarkStackIter(); |
216 | | |
217 | | bool done() const; |
218 | | MarkStack::Tag peekTag() const; |
219 | | MarkStack::TaggedPtr peekPtr() const; |
220 | | MarkStack::ValueArray peekValueArray() const; |
221 | | void next(); |
222 | | void nextPtr(); |
223 | | void nextArray(); |
224 | | |
225 | | // Mutate the current ValueArray to a SavedValueArray. |
226 | | void saveValueArray(const MarkStack::SavedValueArray& savedArray); |
227 | | |
228 | | private: |
229 | | size_t position() const; |
230 | | }; |
231 | | |
232 | | } /* namespace gc */ |
233 | | |
234 | | class GCMarker : public JSTracer |
235 | | { |
236 | | public: |
237 | | explicit GCMarker(JSRuntime* rt); |
238 | | MOZ_MUST_USE bool init(JSGCMode gcMode); |
239 | | |
240 | 0 | void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); } |
241 | 0 | size_t maxCapacity() const { return stack.maxCapacity(); } |
242 | | |
243 | | void start(); |
244 | | void stop(); |
245 | | void reset(); |
246 | | |
247 | | // Mark the given GC thing and traverse its children at some point. |
248 | | template <typename T> void traverse(T thing); |
249 | | |
250 | | // Calls traverse on target after making additional assertions. |
251 | | template <typename S, typename T> void traverseEdge(S source, T* target); |
252 | | template <typename S, typename T> void traverseEdge(S source, const T& target); |
253 | | |
254 | | // Helper methods that coerce their second argument to the base pointer |
255 | | // type. |
256 | 1.31k | template <typename S> void traverseObjectEdge(S source, JSObject* target) { |
257 | 1.31k | traverseEdge(source, target); |
258 | 1.31k | } |
259 | 4.87k | template <typename S> void traverseStringEdge(S source, JSString* target) { |
260 | 4.87k | traverseEdge(source, target); |
261 | 4.87k | } |
262 | | |
263 | | // Notes a weak graph edge for later sweeping. |
264 | | template <typename T> void noteWeakEdge(T* edge); |
265 | | |
266 | | /* |
267 | | * Care must be taken changing the mark color from gray to black. The cycle |
268 | | * collector depends on the invariant that there are no black to gray edges |
269 | | * in the GC heap. This invariant lets the CC not trace through black |
270 | | * objects. If this invariant is violated, the cycle collector may free |
271 | | * objects that are still reachable. |
272 | | */ |
273 | 18 | void setMarkColorGray() { |
274 | 18 | MOZ_ASSERT(isDrained()); |
275 | 18 | MOZ_ASSERT(color == gc::MarkColor::Black); |
276 | 18 | color = gc::MarkColor::Gray; |
277 | 18 | } |
278 | 18 | void setMarkColorBlack() { |
279 | 18 | MOZ_ASSERT(isDrained()); |
280 | 18 | MOZ_ASSERT(color == gc::MarkColor::Gray); |
281 | 18 | color = gc::MarkColor::Black; |
282 | 18 | } |
283 | 3.49M | gc::MarkColor markColor() const { return color; } |
284 | | |
285 | | void enterWeakMarkingMode(); |
286 | | void leaveWeakMarkingMode(); |
287 | 0 | void abortLinearWeakMarking() { |
288 | 0 | leaveWeakMarkingMode(); |
289 | 0 | linearWeakMarkingDisabled_ = true; |
290 | 0 | } |
291 | | |
292 | | void delayMarkingArena(gc::Arena* arena); |
293 | | void delayMarkingChildren(const void* thing); |
294 | | void markDelayedChildren(gc::Arena* arena); |
295 | | MOZ_MUST_USE bool markDelayedChildren(SliceBudget& budget); |
296 | 172 | bool hasDelayedChildren() const { |
297 | 172 | return !!unmarkedArenaStackTop; |
298 | 172 | } |
299 | | |
300 | 0 | bool isDrained() { |
301 | 0 | return isMarkStackEmpty() && !unmarkedArenaStackTop; |
302 | 0 | } |
303 | | |
304 | | MOZ_MUST_USE bool drainMarkStack(SliceBudget& budget); |
305 | | |
306 | 3 | void setGCMode(JSGCMode mode) { stack.setGCMode(mode); } |
307 | | |
308 | | size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; |
309 | | |
310 | | #ifdef DEBUG |
311 | | |
312 | | bool shouldCheckCompartments() { return strictCompartmentChecking; } |
313 | | |
314 | | JS::Zone* stackContainsCrossZonePointerTo(const gc::Cell* cell); |
315 | | |
316 | | #endif |
317 | | |
318 | | void markEphemeronValues(gc::Cell* markedCell, gc::WeakEntryVector& entry); |
319 | | |
320 | 3.44M | static GCMarker* fromTracer(JSTracer* trc) { |
321 | 3.44M | MOZ_ASSERT(trc->isMarkingTracer()); |
322 | 3.44M | return static_cast<GCMarker*>(trc); |
323 | 3.44M | } |
324 | | |
325 | | private: |
326 | | #ifdef DEBUG |
327 | | void checkZone(void* p); |
328 | | #else |
329 | 17.5k | void checkZone(void* p) {} |
330 | | #endif |
331 | | |
332 | | // Push an object onto the stack for later tracing and assert that it has |
333 | | // already been marked. |
334 | | inline void repush(JSObject* obj); |
335 | | |
336 | | template <typename T> void markAndTraceChildren(T* thing); |
337 | | template <typename T> void markAndPush(T* thing); |
338 | | template <typename T> void markAndScan(T* thing); |
339 | | template <typename T> void markImplicitEdgesHelper(T oldThing); |
340 | | template <typename T> void markImplicitEdges(T* oldThing); |
341 | | void eagerlyMarkChildren(JSLinearString* str); |
342 | | void eagerlyMarkChildren(JSRope* rope); |
343 | | void eagerlyMarkChildren(JSString* str); |
344 | | void eagerlyMarkChildren(LazyScript *thing); |
345 | | void eagerlyMarkChildren(Shape* shape); |
346 | | void eagerlyMarkChildren(Scope* scope); |
347 | | void lazilyMarkChildren(ObjectGroup* group); |
348 | | |
349 | | // We may not have concrete types yet, so this has to be outside the header. |
350 | | template <typename T> |
351 | | void dispatchToTraceChildren(T* thing); |
352 | | |
353 | | // Mark the given GC thing, but do not trace its children. Return true |
354 | | // if the thing became marked. |
355 | | template <typename T> |
356 | | MOZ_MUST_USE bool mark(T* thing); |
357 | | |
358 | | template <typename T> |
359 | | inline void pushTaggedPtr(T* ptr); |
360 | | |
361 | | inline void pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end); |
362 | | |
363 | 0 | bool isMarkStackEmpty() { |
364 | 0 | return stack.isEmpty(); |
365 | 0 | } |
366 | | |
367 | | MOZ_MUST_USE bool restoreValueArray(const gc::MarkStack::SavedValueArray& array, |
368 | | HeapSlot** vpp, HeapSlot** endp); |
369 | | gc::MarkStack::ValueArray restoreValueArray(const gc::MarkStack::SavedValueArray& savedArray); |
370 | | |
371 | | void saveValueRanges(); |
372 | | gc::MarkStack::SavedValueArray saveValueRange(const gc::MarkStack::ValueArray& array); |
373 | | |
374 | | inline void processMarkStackTop(SliceBudget& budget); |
375 | | |
376 | | /* The mark stack. Pointers in this stack are "gray" in the GC sense. */ |
377 | | gc::MarkStack stack; |
378 | | |
379 | | /* The color is only applied to objects and functions. */ |
380 | | MainThreadData<gc::MarkColor> color; |
381 | | |
382 | | /* Pointer to the top of the stack of arenas we are delaying marking on. */ |
383 | | MainThreadData<js::gc::Arena*> unmarkedArenaStackTop; |
384 | | |
385 | | /* |
386 | | * If the weakKeys table OOMs, disable the linear algorithm and fall back |
387 | | * to iterating until the next GC. |
388 | | */ |
389 | | MainThreadData<bool> linearWeakMarkingDisabled_; |
390 | | |
391 | | #ifdef DEBUG |
392 | | /* Count of arenas that are currently in the stack. */ |
393 | | MainThreadData<size_t> markLaterArenas; |
394 | | |
395 | | /* Assert that start and stop are called with correct ordering. */ |
396 | | MainThreadData<bool> started; |
397 | | |
398 | | /* |
399 | | * If this is true, all marked objects must belong to a compartment being |
400 | | * GCed. This is used to look for compartment bugs. |
401 | | */ |
402 | | MainThreadData<bool> strictCompartmentChecking; |
403 | | #endif // DEBUG |
404 | | }; |
405 | | |
406 | | } /* namespace js */ |
407 | | |
408 | | // Exported for Tracer.cpp |
409 | 10.5M | inline bool ThingIsPermanentAtomOrWellKnownSymbol(js::gc::Cell* thing) { return false; } |
410 | | bool ThingIsPermanentAtomOrWellKnownSymbol(JSString*); |
411 | | bool ThingIsPermanentAtomOrWellKnownSymbol(JSFlatString*); |
412 | | bool ThingIsPermanentAtomOrWellKnownSymbol(JSLinearString*); |
413 | | bool ThingIsPermanentAtomOrWellKnownSymbol(JSAtom*); |
414 | | bool ThingIsPermanentAtomOrWellKnownSymbol(js::PropertyName*); |
415 | | bool ThingIsPermanentAtomOrWellKnownSymbol(JS::Symbol*); |
416 | | |
417 | | #endif /* gc_GCMarker_h */ |