LCOV - code coverage report
Current view: top level - js/src/gc - AtomMarking.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 71 119 59.7 %
Date: 2017-07-14 16:53:18 Functions: 14 20 70.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13