blob: 067b4ebd84afa3109c7b171873213713696206c6 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef JSGC_GENERATIONAL
#include "jsgc.h"
#include "gc/Barrier-inl.h"
#include "gc/StoreBuffer.h"
#include "vm/ForkJoin.h"
#include "vm/ObjectImpl-inl.h"
using namespace js;
using namespace js::gc;
/*** SlotEdge ***/
JS_ALWAYS_INLINE HeapSlot *
StoreBuffer::SlotEdge::slotLocation() const
{
if (kind == HeapSlot::Element) {
if (offset >= object->getDenseInitializedLength())
return NULL;
return (HeapSlot *)&object->getDenseElement(offset);
}
if (offset >= object->slotSpan())
return NULL;
return &object->getSlotRef(offset);
}
JS_ALWAYS_INLINE void *
StoreBuffer::SlotEdge::deref() const
{
HeapSlot *loc = slotLocation();
return (loc && loc->isGCThing()) ? loc->toGCThing() : NULL;
}
JS_ALWAYS_INLINE void *
StoreBuffer::SlotEdge::location() const
{
return (void *)slotLocation();
}
JS_ALWAYS_INLINE bool
StoreBuffer::SlotEdge::inRememberedSet(const Nursery &nursery) const
{
return !nursery.isInside(object) && nursery.isInside(deref());
}
JS_ALWAYS_INLINE bool
StoreBuffer::SlotEdge::isNullEdge() const
{
return !deref();
}
void
StoreBuffer::WholeCellEdges::mark(JSTracer *trc)
{
JSGCTraceKind kind = GetGCThingTraceKind(tenured);
if (kind <= JSTRACE_OBJECT) {
MarkChildren(trc, static_cast<JSObject *>(tenured));
return;
}
JS_ASSERT(kind == JSTRACE_IONCODE);
static_cast<jit::IonCode *>(tenured)->trace(trc);
}
/*** MonoTypeBuffer ***/
/* How full we allow a store buffer to become before we request a MinorGC. */
const static double HighwaterRatio = 7.0 / 8.0;
template <typename T>
bool
StoreBuffer::MonoTypeBuffer<T>::enable(uint8_t *region, size_t len)
{
JS_ASSERT(len % sizeof(T) == 0);
base = pos = reinterpret_cast<T *>(region);
top = reinterpret_cast<T *>(region + len);
highwater = reinterpret_cast<T *>(region + size_t(double(len) * HighwaterRatio));
JS_ASSERT(highwater > base);
JS_ASSERT(highwater < top);
return true;
}
template <typename T>
void
StoreBuffer::MonoTypeBuffer<T>::disable()
{
base = pos = top = highwater = NULL;
}
template <typename T>
void
StoreBuffer::MonoTypeBuffer<T>::clear()
{
pos = base;
}
template <typename T>
void
StoreBuffer::MonoTypeBuffer<T>::compactNotInSet(const Nursery &nursery)
{
T *insert = base;
for (T *v = base; v != pos; ++v) {
if (v->inRememberedSet(nursery))
*insert++ = *v;
}
pos = insert;
}
template <typename T>
void
StoreBuffer::MonoTypeBuffer<T>::compactRemoveDuplicates()
{
JS_ASSERT(duplicates.empty());
T *insert = base;
for (T *v = base; v != pos; ++v) {
if (!duplicates.has(v->location())) {
*insert++ = *v;
/* Failure to insert will leave the set with duplicates. Oh well. */
duplicates.put(v->location());
}
}
pos = insert;
duplicates.clear();
}
template <typename T>
void
StoreBuffer::MonoTypeBuffer<T>::compact()
{
compactNotInSet(owner->runtime->gcNursery);
compactRemoveDuplicates();
}
template <typename T>
void
StoreBuffer::MonoTypeBuffer<T>::mark(JSTracer *trc)
{
compact();
T *cursor = base;
while (cursor != pos) {
T edge = *cursor++;
if (edge.isNullEdge())
continue;
edge.mark(trc);
}
}
namespace js {
namespace gc {
class AccumulateEdgesTracer : public JSTracer
{
EdgeSet *edges;
static void tracer(JSTracer *jstrc, void **thingp, JSGCTraceKind kind) {
AccumulateEdgesTracer *trc = static_cast<AccumulateEdgesTracer *>(jstrc);
trc->edges->put(thingp);
}
public:
AccumulateEdgesTracer(JSRuntime *rt, EdgeSet *edgesArg) : edges(edgesArg) {
JS_TracerInit(this, rt, AccumulateEdgesTracer::tracer);
}
};
} /* namespace gc */
} /* namespace js */
/*** RelocatableMonoTypeBuffer ***/
template <typename T>
void
StoreBuffer::RelocatableMonoTypeBuffer<T>::compactMoved()
{
for (T *v = this->base; v != this->pos; ++v) {
if (v->isTagged()) {
T match = v->untagged();
for (T *r = this->base; r != v; ++r) {
T check = r->untagged();
if (check == match)
*r = NULL;
}
*v = NULL;
}
}
T *insert = this->base;
for (T *cursor = this->base; cursor != this->pos; ++cursor) {
if (*cursor != NULL)
*insert++ = *cursor;
}
this->pos = insert;
#ifdef DEBUG
for (T *cursor = this->base; cursor != this->pos; ++cursor)
JS_ASSERT(!cursor->isTagged());
#endif
}
template <typename T>
void
StoreBuffer::RelocatableMonoTypeBuffer<T>::compact()
{
compactMoved();
StoreBuffer::MonoTypeBuffer<T>::compact();
}
/*** GenericBuffer ***/
bool
StoreBuffer::GenericBuffer::enable(uint8_t *region, size_t len)
{
base = pos = region;
top = region + len;
return true;
}
void
StoreBuffer::GenericBuffer::disable()
{
base = pos = top = NULL;
}
void
StoreBuffer::GenericBuffer::clear()
{
pos = base;
}
void
StoreBuffer::GenericBuffer::mark(JSTracer *trc)
{
uint8_t *p = base;
while (p < pos) {
unsigned size = *((unsigned *)p);
p += sizeof(unsigned);
BufferableRef *edge = reinterpret_cast<BufferableRef *>(p);
edge->mark(trc);
p += size;
}
}
/*** Edges ***/
void
StoreBuffer::CellPtrEdge::mark(JSTracer *trc)
{
MarkObjectRoot(trc, reinterpret_cast<JSObject**>(edge), "store buffer edge");
}
void
StoreBuffer::ValueEdge::mark(JSTracer *trc)
{
MarkValueRoot(trc, edge, "store buffer edge");
}
void
StoreBuffer::SlotEdge::mark(JSTracer *trc)
{
if (kind == HeapSlot::Element)
MarkSlot(trc, (HeapSlot*)&object->getDenseElement(offset), "store buffer edge");
else
MarkSlot(trc, &object->getSlotRef(offset), "store buffer edge");
}
/*** StoreBuffer ***/
bool
StoreBuffer::enable()
{
if (enabled)
return true;
buffer = js_malloc(TotalSize);
if (!buffer)
return false;
/* Initialize the individual edge buffers in sub-regions. */
uint8_t *asBytes = static_cast<uint8_t *>(buffer);
size_t offset = 0;
if (!bufferVal.enable(&asBytes[offset], ValueBufferSize))
return false;
offset += ValueBufferSize;
if (!bufferCell.enable(&asBytes[offset], CellBufferSize))
return false;
offset += CellBufferSize;
if (!bufferSlot.enable(&asBytes[offset], SlotBufferSize))
return false;
offset += SlotBufferSize;
if (!bufferWholeCell.enable(&asBytes[offset], WholeCellBufferSize))
return false;
offset += WholeCellBufferSize;
if (!bufferRelocVal.enable(&asBytes[offset], RelocValueBufferSize))
return false;
offset += RelocValueBufferSize;
if (!bufferRelocCell.enable(&asBytes[offset], RelocCellBufferSize))
return false;
offset += RelocCellBufferSize;
if (!bufferGeneric.enable(&asBytes[offset], GenericBufferSize))
return false;
offset += GenericBufferSize;
JS_ASSERT(offset == TotalSize);
enabled = true;
return true;
}
void
StoreBuffer::disable()
{
if (!enabled)
return;
aboutToOverflow = false;
bufferVal.disable();
bufferCell.disable();
bufferSlot.disable();
bufferWholeCell.disable();
bufferRelocVal.disable();
bufferRelocCell.disable();
bufferGeneric.disable();
js_free(buffer);
enabled = false;
overflowed = false;
}
bool
StoreBuffer::clear()
{
if (!enabled)
return true;
aboutToOverflow = false;
bufferVal.clear();
bufferCell.clear();
bufferSlot.clear();
bufferWholeCell.clear();
bufferRelocVal.clear();
bufferRelocCell.clear();
bufferGeneric.clear();
return true;
}
void
StoreBuffer::mark(JSTracer *trc)
{
JS_ASSERT(isEnabled());
JS_ASSERT(!overflowed);
bufferVal.mark(trc);
bufferCell.mark(trc);
bufferSlot.mark(trc);
bufferWholeCell.mark(trc);
bufferRelocVal.mark(trc);
bufferRelocCell.mark(trc);
bufferGeneric.mark(trc);
}
void
StoreBuffer::setAboutToOverflow()
{
aboutToOverflow = true;
runtime->triggerOperationCallback();
}
void
StoreBuffer::setOverflowed()
{
JS_ASSERT(enabled);
overflowed = true;
}
bool
StoreBuffer::inParallelSection() const
{
return InParallelSection();
}
JS_PUBLIC_API(void)
JS::HeapCellPostBarrier(js::gc::Cell **cellp)
{
JS_ASSERT(*cellp);
JSRuntime *runtime = (*cellp)->runtime();
runtime->gcStoreBuffer.putRelocatableCell(cellp);
}
JS_PUBLIC_API(void)
JS::HeapCellRelocate(js::gc::Cell **cellp)
{
/* Called with old contents of *pp before overwriting. */
JS_ASSERT(*cellp);
JSRuntime *runtime = (*cellp)->runtime();
runtime->gcStoreBuffer.removeRelocatableCell(cellp);
}
JS_PUBLIC_API(void)
JS::HeapValuePostBarrier(JS::Value *valuep)
{
JS_ASSERT(JSVAL_IS_TRACEABLE(*valuep));
JSRuntime *runtime = static_cast<js::gc::Cell *>(valuep->toGCThing())->runtime();
runtime->gcStoreBuffer.putRelocatableValue(valuep);
}
JS_PUBLIC_API(void)
JS::HeapValueRelocate(JS::Value *valuep)
{
/* Called with old contents of *valuep before overwriting. */
JS_ASSERT(JSVAL_IS_TRACEABLE(*valuep));
JSRuntime *runtime = static_cast<js::gc::Cell *>(valuep->toGCThing())->runtime();
runtime->gcStoreBuffer.removeRelocatableValue(valuep);
}
template class StoreBuffer::MonoTypeBuffer<StoreBuffer::ValueEdge>;
template class StoreBuffer::MonoTypeBuffer<StoreBuffer::CellPtrEdge>;
template class StoreBuffer::MonoTypeBuffer<StoreBuffer::SlotEdge>;
template class StoreBuffer::MonoTypeBuffer<StoreBuffer::WholeCellEdges>;
template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::ValueEdge>;
template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::CellPtrEdge>;
#endif /* JSGC_GENERATIONAL */