Changeset View
Changeset View
Standalone View
Standalone View
libraries/source/spidermonkey/include-win32-debug/js/HeapAPI.h
Show All 11 Lines | |||||
#include "jspubtd.h" | #include "jspubtd.h" | ||||
#include "js/TraceKind.h" | #include "js/TraceKind.h" | ||||
#include "js/Utility.h" | #include "js/Utility.h" | ||||
/* These values are private to the JS engine. */ | /* These values are private to the JS engine. */ | ||||
namespace js { | namespace js { | ||||
// Whether the current thread is permitted access to any part of the specified | |||||
// runtime or zone. | |||||
JS_FRIEND_API(bool) | |||||
CurrentThreadCanAccessRuntime(JSRuntime* rt); | |||||
JS_FRIEND_API(bool) | JS_FRIEND_API(bool) | ||||
CurrentThreadCanAccessZone(JS::Zone* zone); | CurrentThreadCanAccessZone(JS::Zone* zone); | ||||
namespace gc { | namespace gc { | ||||
struct Cell; | struct Cell; | ||||
const size_t ArenaShift = 12; | const size_t ArenaShift = 12; | ||||
Show All 18 Lines | |||||
const size_t ChunkMarkBitmapBits = 31744; | const size_t ChunkMarkBitmapBits = 31744; | ||||
#else | #else | ||||
const size_t ChunkMarkBitmapOffset = 1032352; | const size_t ChunkMarkBitmapOffset = 1032352; | ||||
const size_t ChunkMarkBitmapBits = 129024; | const size_t ChunkMarkBitmapBits = 129024; | ||||
#endif | #endif | ||||
const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*); | const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*); | ||||
const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t); | const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t); | ||||
const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize; | const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize; | ||||
const size_t ArenaZoneOffset = 0; | const size_t ArenaZoneOffset = sizeof(size_t); | ||||
const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) + | |||||
sizeof(size_t) + sizeof(uintptr_t); | |||||
/* | /* | ||||
* Live objects are marked black. How many other additional colors are available | * Live objects are marked black. How many other additional colors are available | ||||
* depends on the size of the GCThing. Objects marked gray are eligible for | * depends on the size of the GCThing. Objects marked gray are eligible for | ||||
* cycle collection. | * cycle collection. | ||||
*/ | */ | ||||
static const uint32_t BLACK = 0; | static const uint32_t BLACK = 0; | ||||
static const uint32_t GRAY = 1; | static const uint32_t GRAY = 1; | ||||
/* | /* | ||||
* The "location" field in the Chunk trailer is a bit vector indicting various | * The "location" field in the Chunk trailer is a enum indicating various roles | ||||
* roles of the chunk. | * of the chunk. | ||||
* | |||||
* The value 0 for the "location" field is invalid, at least one bit must be | |||||
* set. | |||||
* | |||||
* Some bits preclude others, for example, any "nursery" bit precludes any | |||||
* "tenured" or "middle generation" bit. | |||||
*/ | */ | ||||
const uintptr_t ChunkLocationBitNursery = 1; // Standard GGC nursery | enum class ChunkLocation : uint32_t | ||||
const uintptr_t ChunkLocationBitTenuredHeap = 2; // Standard GGC tenured generation | { | ||||
Invalid = 0, | |||||
const uintptr_t ChunkLocationAnyNursery = ChunkLocationBitNursery; | Nursery = 1, | ||||
TenuredHeap = 2 | |||||
}; | |||||
#ifdef JS_DEBUG | #ifdef JS_DEBUG | ||||
/* When downcasting, ensure we are actually the right type. */ | /* When downcasting, ensure we are actually the right type. */ | ||||
extern JS_FRIEND_API(void) | extern JS_FRIEND_API(void) | ||||
AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind); | AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind); | ||||
#else | #else | ||||
inline void | inline void | ||||
AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) {} | AssertGCThingHasType(js::gc::Cell* cell, JS::TraceKind kind) {} | ||||
Show All 17 Lines | |||||
struct Zone | struct Zone | ||||
{ | { | ||||
protected: | protected: | ||||
JSRuntime* const runtime_; | JSRuntime* const runtime_; | ||||
JSTracer* const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|. | JSTracer* const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|. | ||||
public: | public: | ||||
// Stack GC roots for Rooted GC pointers. | |||||
js::RootedListHeads stackRoots_; | |||||
template <typename T> friend class JS::Rooted; | |||||
bool needsIncrementalBarrier_; | bool needsIncrementalBarrier_; | ||||
Zone(JSRuntime* runtime, JSTracer* barrierTracerArg) | Zone(JSRuntime* runtime, JSTracer* barrierTracerArg) | ||||
: runtime_(runtime), | : runtime_(runtime), | ||||
barrierTracer_(barrierTracerArg), | barrierTracer_(barrierTracerArg), | ||||
needsIncrementalBarrier_(false) | needsIncrementalBarrier_(false) | ||||
{} | { | ||||
for (auto& stackRootPtr : stackRoots_) | |||||
stackRootPtr = nullptr; | |||||
} | |||||
bool needsIncrementalBarrier() const { | bool needsIncrementalBarrier() const { | ||||
return needsIncrementalBarrier_; | return needsIncrementalBarrier_; | ||||
} | } | ||||
JSTracer* barrierTracer() { | JSTracer* barrierTracer() { | ||||
MOZ_ASSERT(needsIncrementalBarrier_); | MOZ_ASSERT(needsIncrementalBarrier_); | ||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_)); | MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_)); | ||||
return barrierTracer_; | return barrierTracer_; | ||||
} | } | ||||
JSRuntime* runtimeFromMainThread() const { | JSRuntime* runtimeFromMainThread() const { | ||||
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_)); | MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_)); | ||||
return runtime_; | return runtime_; | ||||
} | } | ||||
// Note: Unrestricted access to the zone's runtime from an arbitrary | // Note: Unrestricted access to the zone's runtime from an arbitrary | ||||
// thread can easily lead to races. Use this method very carefully. | // thread can easily lead to races. Use this method very carefully. | ||||
JSRuntime* runtimeFromAnyThread() const { | JSRuntime* runtimeFromAnyThread() const { | ||||
return runtime_; | return runtime_; | ||||
} | } | ||||
static JS::shadow::Zone* asShadowZone(JS::Zone* zone) { | static MOZ_ALWAYS_INLINE JS::shadow::Zone* asShadowZone(JS::Zone* zone) { | ||||
return reinterpret_cast<JS::shadow::Zone*>(zone); | return reinterpret_cast<JS::shadow::Zone*>(zone); | ||||
} | } | ||||
}; | }; | ||||
} /* namespace shadow */ | } /* namespace shadow */ | ||||
/** | /** | ||||
* A GC pointer, tagged with the trace kind. | * A GC pointer, tagged with the trace kind. | ||||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | |||||
static MOZ_ALWAYS_INLINE uintptr_t* | static MOZ_ALWAYS_INLINE uintptr_t* | ||||
GetGCThingMarkBitmap(const uintptr_t addr) | GetGCThingMarkBitmap(const uintptr_t addr) | ||||
{ | { | ||||
MOZ_ASSERT(addr); | MOZ_ASSERT(addr); | ||||
const uintptr_t bmap_addr = (addr & ~ChunkMask) | ChunkMarkBitmapOffset; | const uintptr_t bmap_addr = (addr & ~ChunkMask) | ChunkMarkBitmapOffset; | ||||
return reinterpret_cast<uintptr_t*>(bmap_addr); | return reinterpret_cast<uintptr_t*>(bmap_addr); | ||||
} | } | ||||
static MOZ_ALWAYS_INLINE JS::shadow::Runtime* | |||||
GetGCThingRuntime(const uintptr_t addr) | |||||
{ | |||||
MOZ_ASSERT(addr); | |||||
const uintptr_t rt_addr = (addr & ~ChunkMask) | ChunkRuntimeOffset; | |||||
return *reinterpret_cast<JS::shadow::Runtime**>(rt_addr); | |||||
} | |||||
static MOZ_ALWAYS_INLINE void | static MOZ_ALWAYS_INLINE void | ||||
GetGCThingMarkWordAndMask(const uintptr_t addr, uint32_t color, | GetGCThingMarkWordAndMask(const uintptr_t addr, uint32_t color, | ||||
uintptr_t** wordp, uintptr_t* maskp) | uintptr_t** wordp, uintptr_t* maskp) | ||||
{ | { | ||||
MOZ_ASSERT(addr); | MOZ_ASSERT(addr); | ||||
const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellSize + color; | const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellSize + color; | ||||
MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits); | MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits); | ||||
uintptr_t* bitmap = GetGCThingMarkBitmap(addr); | uintptr_t* bitmap = GetGCThingMarkBitmap(addr); | ||||
const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT; | const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT; | ||||
*maskp = uintptr_t(1) << (bit % nbits); | *maskp = uintptr_t(1) << (bit % nbits); | ||||
*wordp = &bitmap[bit / nbits]; | *wordp = &bitmap[bit / nbits]; | ||||
} | } | ||||
static MOZ_ALWAYS_INLINE JS::Zone* | static MOZ_ALWAYS_INLINE JS::Zone* | ||||
GetGCThingZone(const uintptr_t addr) | GetGCThingZone(const uintptr_t addr) | ||||
{ | { | ||||
MOZ_ASSERT(addr); | MOZ_ASSERT(addr); | ||||
const uintptr_t zone_addr = (addr & ~ArenaMask) | ArenaZoneOffset; | const uintptr_t zone_addr = (addr & ~ArenaMask) | ArenaZoneOffset; | ||||
return *reinterpret_cast<JS::Zone**>(zone_addr); | return *reinterpret_cast<JS::Zone**>(zone_addr); | ||||
} | } | ||||
static MOZ_ALWAYS_INLINE JS::shadow::Runtime* | |||||
GetCellRuntime(const Cell* cell) | |||||
{ | |||||
MOZ_ASSERT(cell); | |||||
const uintptr_t addr = uintptr_t(cell); | |||||
const uintptr_t rt_addr = (addr & ~ChunkMask) | ChunkRuntimeOffset; | |||||
return *reinterpret_cast<JS::shadow::Runtime**>(rt_addr); | |||||
} | |||||
static MOZ_ALWAYS_INLINE bool | static MOZ_ALWAYS_INLINE bool | ||||
CellIsMarkedGray(const Cell* cell) | CellIsMarkedGray(const Cell* cell) | ||||
{ | { | ||||
MOZ_ASSERT(cell); | MOZ_ASSERT(cell); | ||||
MOZ_ASSERT(!js::gc::IsInsideNursery(cell)); | if (js::gc::IsInsideNursery(cell)) | ||||
return false; | |||||
uintptr_t* word, mask; | uintptr_t* word, mask; | ||||
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::GRAY, &word, &mask); | js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::GRAY, &word, &mask); | ||||
return *word & mask; | return *word & mask; | ||||
} | } | ||||
extern JS_PUBLIC_API(bool) | |||||
CellIsMarkedGrayIfKnown(const Cell* cell); | |||||
} /* namespace detail */ | } /* namespace detail */ | ||||
MOZ_ALWAYS_INLINE bool | MOZ_ALWAYS_INLINE bool | ||||
IsInsideNursery(const js::gc::Cell* cell) | IsInsideNursery(const js::gc::Cell* cell) | ||||
{ | { | ||||
if (!cell) | if (!cell) | ||||
return false; | return false; | ||||
uintptr_t addr = uintptr_t(cell); | uintptr_t addr = uintptr_t(cell); | ||||
addr &= ~js::gc::ChunkMask; | addr &= ~js::gc::ChunkMask; | ||||
addr |= js::gc::ChunkLocationOffset; | addr |= js::gc::ChunkLocationOffset; | ||||
uint32_t location = *reinterpret_cast<uint32_t*>(addr); | auto location = *reinterpret_cast<ChunkLocation*>(addr); | ||||
MOZ_ASSERT(location != 0); | MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap); | ||||
return location & ChunkLocationAnyNursery; | return location == ChunkLocation::Nursery; | ||||
} | } | ||||
} /* namespace gc */ | } /* namespace gc */ | ||||
} /* namespace js */ | } /* namespace js */ | ||||
namespace JS { | namespace JS { | ||||
static MOZ_ALWAYS_INLINE Zone* | static MOZ_ALWAYS_INLINE Zone* | ||||
GetTenuredGCThingZone(GCCellPtr thing) | GetTenuredGCThingZone(GCCellPtr thing) | ||||
{ | { | ||||
MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell())); | MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell())); | ||||
return js::gc::detail::GetGCThingZone(thing.unsafeAsUIntPtr()); | return js::gc::detail::GetGCThingZone(thing.unsafeAsUIntPtr()); | ||||
} | } | ||||
static MOZ_ALWAYS_INLINE Zone* | static MOZ_ALWAYS_INLINE Zone* | ||||
GetStringZone(JSString* str) | GetStringZone(JSString* str) | ||||
{ | { | ||||
return js::gc::detail::GetGCThingZone(uintptr_t(str)); | return js::gc::detail::GetGCThingZone(uintptr_t(str)); | ||||
} | } | ||||
extern JS_PUBLIC_API(Zone*) | extern JS_PUBLIC_API(Zone*) | ||||
GetObjectZone(JSObject* obj); | GetObjectZone(JSObject* obj); | ||||
static MOZ_ALWAYS_INLINE bool | static MOZ_ALWAYS_INLINE bool | ||||
ObjectIsTenured(JSObject* obj) | |||||
{ | |||||
return !js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(obj)); | |||||
} | |||||
static MOZ_ALWAYS_INLINE bool | |||||
ObjectIsMarkedGray(JSObject* obj) | |||||
{ | |||||
/* | |||||
* GC things residing in the nursery cannot be gray: they have no mark bits. | |||||
* All live objects in the nursery are moved to tenured at the beginning of | |||||
* each GC slice, so the gray marker never sees nursery things. | |||||
*/ | |||||
if (js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(obj))) | |||||
return false; | |||||
return js::gc::detail::CellIsMarkedGray(reinterpret_cast<js::gc::Cell*>(obj)); | |||||
} | |||||
static MOZ_ALWAYS_INLINE bool | |||||
ScriptIsMarkedGray(JSScript* script) | |||||
{ | |||||
return js::gc::detail::CellIsMarkedGray(reinterpret_cast<js::gc::Cell*>(script)); | |||||
} | |||||
static MOZ_ALWAYS_INLINE bool | |||||
GCThingIsMarkedGray(GCCellPtr thing) | GCThingIsMarkedGray(GCCellPtr thing) | ||||
{ | { | ||||
if (js::gc::IsInsideNursery(thing.asCell())) | |||||
return false; | |||||
if (thing.mayBeOwnedByOtherRuntime()) | if (thing.mayBeOwnedByOtherRuntime()) | ||||
return false; | return false; | ||||
return js::gc::detail::CellIsMarkedGray(thing.asCell()); | return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell()); | ||||
} | } | ||||
extern JS_PUBLIC_API(JS::TraceKind) | |||||
GCThingTraceKind(void* thing); | |||||
} /* namespace JS */ | } /* namespace JS */ | ||||
namespace js { | namespace js { | ||||
namespace gc { | namespace gc { | ||||
static MOZ_ALWAYS_INLINE bool | static MOZ_ALWAYS_INLINE bool | ||||
IsIncrementalBarrierNeededOnTenuredGCThing(JS::shadow::Runtime* rt, const JS::GCCellPtr thing) | IsIncrementalBarrierNeededOnTenuredGCThing(JS::shadow::Runtime* rt, const JS::GCCellPtr thing) | ||||
{ | { | ||||
MOZ_ASSERT(thing); | MOZ_ASSERT(thing); | ||||
MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell())); | MOZ_ASSERT(!js::gc::IsInsideNursery(thing.asCell())); | ||||
if (rt->isHeapBusy()) | |||||
return false; | // TODO: I'd like to assert !isHeapBusy() here but this gets called while we | ||||
// are tracing the heap, e.g. during memory reporting (see bug 1313318). | |||||
MOZ_ASSERT(!rt->isHeapCollecting()); | |||||
JS::Zone* zone = JS::GetTenuredGCThingZone(thing); | JS::Zone* zone = JS::GetTenuredGCThingZone(thing); | ||||
return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier(); | return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier(); | ||||
} | } | ||||
/** | /** | ||||
* Create an object providing access to the garbage collector's internal notion | * Create an object providing access to the garbage collector's internal notion | ||||
* of the current state of memory (both GC heap memory and GCthing-controlled | * of the current state of memory (both GC heap memory and GCthing-controlled | ||||
* malloc memory. | * malloc memory. | ||||
*/ | */ | ||||
extern JS_PUBLIC_API(JSObject*) | extern JS_PUBLIC_API(JSObject*) | ||||
NewMemoryInfoObject(JSContext* cx); | NewMemoryInfoObject(JSContext* cx); | ||||
} /* namespace gc */ | } /* namespace gc */ | ||||
} /* namespace js */ | } /* namespace js */ | ||||
#endif /* js_HeapAPI_h */ | #endif /* js_HeapAPI_h */ |
Wildfire Games · Phabricator