Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/js/src/gc/AtomMarking.cpp
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
#include "gc/AtomMarking-inl.h"
8
9
#include "gc/PublicIterators.h"
10
#include "vm/Realm.h"
11
12
#include "gc/GC-inl.h"
13
#include "gc/Heap-inl.h"
14
15
namespace js {
16
namespace gc {
17
18
// [SMDOC] GC Atom Marking
19
//
20
// Things in the atoms zone (which includes atomized strings and other things,
21
// all of which we will refer to as 'atoms' here) may be pointed to freely by
22
// things in other zones. To avoid the need to perform garbage collections of
23
// the entire runtime to collect atoms, we compute a separate atom mark bitmap
24
// for each zone that is always an overapproximation of the atoms that zone is
25
// using. When an atom is not in the mark bitmap for any zone, it can be
26
// destroyed.
27
//
28
// To minimize interference with the rest of the GC, atom marking and sweeping
29
// is done by manipulating the mark bitmaps in the chunks used for the atoms.
30
// When the atoms zone is being collected, the mark bitmaps for the chunk(s)
31
// used by the atoms are updated normally during marking. After marking
32
// finishes, the chunk mark bitmaps are translated to a more efficient atom mark
33
// bitmap (see below) that is stored on the zones which the GC collected
34
// (computeBitmapFromChunkMarkBits). Before sweeping begins, the chunk mark
35
// bitmaps are updated with any atoms that might be referenced by zones which
36
// weren't collected (markAtomsUsedByUncollectedZones). The GC sweeping will
37
// then release all atoms which are not marked by any zone.
38
//
39
// The representation of atom mark bitmaps is as follows:
40
//
41
// Each arena in the atoms zone has an atomBitmapStart() value indicating the
42
// word index into the bitmap of the first thing in the arena. Each arena uses
43
// ArenaBitmapWords of data to store its bitmap, which uses the same
44
// representation as chunk mark bitmaps: one bit is allocated per Cell, with
45
// bits for space between things being unused when things are larger than a
46
// single Cell.
47
48
void
49
AtomMarkingRuntime::registerArena(Arena* arena, const AutoLockGC& lock)
50
185
{
51
185
    MOZ_ASSERT(arena->getThingSize() != 0);
52
185
    MOZ_ASSERT(arena->getThingSize() % CellAlignBytes == 0);
53
185
    MOZ_ASSERT(arena->zone->isAtomsZone());
54
185
55
185
    // We need to find a range of bits from the atoms bitmap for this arena.
56
185
57
185
    // Look for a free range of bits compatible with this arena.
58
185
    if (freeArenaIndexes.ref().length()) {
59
0
        arena->atomBitmapStart() = freeArenaIndexes.ref().popCopy();
60
0
        return;
61
0
    }
62
185
63
185
    // Allocate a range of bits from the end for this arena.
64
185
    arena->atomBitmapStart() = allocatedWords;
65
185
    allocatedWords += ArenaBitmapWords;
66
185
}
67
68
void
69
AtomMarkingRuntime::unregisterArena(Arena* arena, const AutoLockGC& lock)
70
0
{
71
0
    MOZ_ASSERT(arena->zone->isAtomsZone());
72
0
73
0
    // Leak these atom bits if we run out of memory.
74
0
    mozilla::Unused << freeArenaIndexes.ref().emplaceBack(arena->atomBitmapStart());
75
0
}
76
77
bool
78
AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, DenseBitmap& bitmap)
79
0
{
80
0
    MOZ_ASSERT(CurrentThreadIsPerformingGC());
81
0
    MOZ_ASSERT(!runtime->hasHelperThreadZones());
82
0
83
0
    if (!bitmap.ensureSpace(allocatedWords)) {
84
0
        return false;
85
0
    }
86
0
87
0
    Zone* atomsZone = runtime->unsafeAtomsZone();
88
0
    for (auto thingKind : AllAllocKinds()) {
89
0
        for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) {
90
0
            Arena* arena = aiter.get();
91
0
            uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena);
92
0
            bitmap.copyBitsFrom(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
93
0
        }
94
0
    }
95
0
96
0
    return true;
97
0
}
98
99
void
100
AtomMarkingRuntime::refineZoneBitmapForCollectedZone(Zone* zone, const DenseBitmap& bitmap)
101
0
{
102
0
    MOZ_ASSERT(zone->isCollectingFromAnyThread());
103
0
104
0
    if (zone->isAtomsZone()) {
105
0
        return;
106
0
    }
107
0
108
0
    // Take the bitwise and between the two mark bitmaps to get the best new
109
0
    // overapproximation we can. |bitmap| might include bits that are not in
110
0
    // the zone's mark bitmap, if additional zones were collected by the GC.
111
0
    zone->markedAtoms().bitwiseAndWith(bitmap);
112
0
}
113
114
// Set any bits in the chunk mark bitmaps for atoms which are marked in bitmap.
115
template <typename Bitmap>
116
static void
117
BitwiseOrIntoChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap)
118
0
{
119
0
    // Make sure that by copying the mark bits for one arena in word sizes we
120
0
    // do not affect the mark bits for other arenas.
121
0
    static_assert(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD,
122
0
                  "ArenaBitmapWords must evenly divide ArenaBitmapBits");
123
0
124
0
    Zone* atomsZone = runtime->unsafeAtomsZone();
125
0
    for (auto thingKind : AllAllocKinds()) {
126
0
        for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) {
127
0
            Arena* arena = aiter.get();
128
0
            uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena);
129
0
            bitmap.bitwiseOrRangeInto(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
130
0
        }
131
0
    }
132
0
}
Unexecuted instantiation: Unified_cpp_js_src8.cpp:void js::gc::BitwiseOrIntoChunkMarkBits<js::DenseBitmap>(JSRuntime*, js::DenseBitmap&)
Unexecuted instantiation: Unified_cpp_js_src8.cpp:void js::gc::BitwiseOrIntoChunkMarkBits<js::SparseBitmap>(JSRuntime*, js::SparseBitmap&)
133
134
void
135
AtomMarkingRuntime::markAtomsUsedByUncollectedZones(JSRuntime* runtime)
136
0
{
137
0
    MOZ_ASSERT(CurrentThreadIsPerformingGC());
138
0
    MOZ_ASSERT(!runtime->hasHelperThreadZones());
139
0
140
0
    // Try to compute a simple union of the zone atom bitmaps before updating
141
0
    // the chunk mark bitmaps. If this allocation fails then fall back to
142
0
    // updating the chunk mark bitmaps separately for each zone.
143
0
    DenseBitmap markedUnion;
144
0
    if (markedUnion.ensureSpace(allocatedWords)) {
145
0
        for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
146
0
            // We only need to update the chunk mark bits for zones which were
147
0
            // not collected in the current GC. Atoms which are referenced by
148
0
            // collected zones have already been marked.
149
0
            if (!zone->isCollectingFromAnyThread()) {
150
0
                zone->markedAtoms().bitwiseOrInto(markedUnion);
151
0
            }
152
0
        }
153
0
        BitwiseOrIntoChunkMarkBits(runtime, markedUnion);
154
0
    } else {
155
0
        for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
156
0
            if (!zone->isCollectingFromAnyThread()) {
157
0
                BitwiseOrIntoChunkMarkBits(runtime, zone->markedAtoms());
158
0
            }
159
0
        }
160
0
    }
161
0
}
162
163
template <typename T>
164
void
165
AtomMarkingRuntime::markAtom(JSContext* cx, T* thing)
166
7.81k
{
167
7.81k
    return inlinedMarkAtom(cx, thing);
168
7.81k
}
void js::gc::AtomMarkingRuntime::markAtom<JSAtom>(JSContext*, JSAtom*)
Line
Count
Source
166
7.77k
{
167
7.77k
    return inlinedMarkAtom(cx, thing);
168
7.77k
}
void js::gc::AtomMarkingRuntime::markAtom<JS::Symbol>(JSContext*, JS::Symbol*)
Line
Count
Source
166
36
{
167
36
    return inlinedMarkAtom(cx, thing);
168
36
}
169
170
template void AtomMarkingRuntime::markAtom(JSContext* cx, JSAtom* thing);
171
template void AtomMarkingRuntime::markAtom(JSContext* cx, JS::Symbol* thing);
172
173
void
174
AtomMarkingRuntime::markId(JSContext* cx, jsid id)
175
2
{
176
2
    if (JSID_IS_ATOM(id)) {
177
2
        markAtom(cx, JSID_TO_ATOM(id));
178
2
        return;
179
2
    }
180
0
    if (JSID_IS_SYMBOL(id)) {
181
0
        markAtom(cx, JSID_TO_SYMBOL(id));
182
0
        return;
183
0
    }
184
0
    MOZ_ASSERT(!JSID_IS_GCTHING(id));
185
0
}
186
187
void
188
AtomMarkingRuntime::markAtomValue(JSContext* cx, const Value& value)
189
2
{
190
2
    if (value.isString()) {
191
0
        if (value.toString()->isAtom()) {
192
0
            markAtom(cx, &value.toString()->asAtom());
193
0
        }
194
0
        return;
195
0
    }
196
2
    if (value.isSymbol()) {
197
0
        markAtom(cx, value.toSymbol());
198
0
        return;
199
0
    }
200
2
    MOZ_ASSERT_IF(value.isGCThing(),
201
2
                  value.isObject() ||
202
2
                  value.isPrivateGCThing() ||
203
2
                  IF_BIGINT(value.isBigInt(), false));
204
2
}
205
206
void
207
AtomMarkingRuntime::adoptMarkedAtoms(Zone* target, Zone* source)
208
0
{
209
0
    MOZ_ASSERT(CurrentThreadCanAccessZone(source));
210
0
    MOZ_ASSERT(CurrentThreadCanAccessZone(target));
211
0
    target->markedAtoms().bitwiseOrWith(source->markedAtoms());
212
0
}
213
214
#ifdef DEBUG
215
template <typename T>
216
bool
217
AtomMarkingRuntime::atomIsMarked(Zone* zone, T* thing)
218
{
219
    static_assert(mozilla::IsSame<T, JSAtom>::value ||
220
                  mozilla::IsSame<T, JS::Symbol>::value,
221
                  "Should only be called with JSAtom* or JS::Symbol* argument");
222
223
    MOZ_ASSERT(thing);
224
    MOZ_ASSERT(!IsInsideNursery(thing));
225
    MOZ_ASSERT(thing->zoneFromAnyThread()->isAtomsZone());
226
227
    if (!zone->runtimeFromAnyThread()->permanentAtomsPopulated()) {
228
        return true;
229
    }
230
231
    if (ThingIsPermanent(thing)) {
232
        return true;
233
    }
234
235
    size_t bit = GetAtomBit(&thing->asTenured());
236
    return zone->markedAtoms().getBit(bit);
237
}
238
239
template bool AtomMarkingRuntime::atomIsMarked(Zone* zone, JSAtom* thing);
240
template bool AtomMarkingRuntime::atomIsMarked(Zone* zone, JS::Symbol* thing);
241
242
template<>
243
bool
244
AtomMarkingRuntime::atomIsMarked(Zone* zone, TenuredCell* thing)
245
{
246
    if (!thing) {
247
        return true;
248
    }
249
250
    if (thing->is<JSString>()) {
251
        JSString* str = thing->as<JSString>();
252
        if (!str->isAtom()) {
253
            return true;
254
        }
255
        return atomIsMarked(zone, &str->asAtom());
256
    }
257
258
    if (thing->is<JS::Symbol>()) {
259
        return atomIsMarked(zone, thing->as<JS::Symbol>());
260
    }
261
262
    return true;
263
}
264
265
bool
266
AtomMarkingRuntime::idIsMarked(Zone* zone, jsid id)
267
{
268
    if (JSID_IS_ATOM(id)) {
269
        return atomIsMarked(zone, JSID_TO_ATOM(id));
270
    }
271
272
    if (JSID_IS_SYMBOL(id)) {
273
        return atomIsMarked(zone, JSID_TO_SYMBOL(id));
274
    }
275
276
    MOZ_ASSERT(!JSID_IS_GCTHING(id));
277
    return true;
278
}
279
280
bool
281
AtomMarkingRuntime::valueIsMarked(Zone* zone, const Value& value)
282
{
283
    if (value.isString()) {
284
        if (value.toString()->isAtom()) {
285
            return atomIsMarked(zone, &value.toString()->asAtom());
286
        }
287
        return true;
288
    }
289
290
    if (value.isSymbol()) {
291
        return atomIsMarked(zone, value.toSymbol());
292
    }
293
294
    MOZ_ASSERT_IF(value.isGCThing(),
295
                  value.isObject() ||
296
                  value.isPrivateGCThing() ||
297
                  IF_BIGINT(value.isBigInt(), false));
298
    return true;
299
}
300
301
#endif // DEBUG
302
303
} // namespace gc
304
305
#ifdef DEBUG
306
307
bool
308
AtomIsMarked(Zone* zone, JSAtom* atom)
309
{
310
    return zone->runtimeFromAnyThread()->gc.atomMarking.atomIsMarked(zone, atom);
311
}
312
313
bool
314
AtomIsMarked(Zone* zone, jsid id)
315
{
316
    return zone->runtimeFromAnyThread()->gc.atomMarking.idIsMarked(zone, id);
317
}
318
319
bool
320
AtomIsMarked(Zone* zone, const Value& value)
321
{
322
    return zone->runtimeFromAnyThread()->gc.atomMarking.valueIsMarked(zone, value);
323
}
324
325
#endif // DEBUG
326
327
} // namespace js