зеркало из https://github.com/mozilla/gecko-dev.git
Backout 189816733310 for Android bustage.
This commit is contained in:
Родитель
996b8eac60
Коммит
0bc926303e
|
@ -139,7 +139,6 @@ CPPSRCS = \
|
|||
Marking.cpp \
|
||||
Memory.cpp \
|
||||
Statistics.cpp \
|
||||
StoreBuffer.cpp \
|
||||
StringBuffer.cpp \
|
||||
Unicode.cpp \
|
||||
Xdr.cpp \
|
||||
|
@ -192,7 +191,6 @@ EXPORTS_gc = \
|
|||
Heap.h \
|
||||
Root.h \
|
||||
Statistics.h \
|
||||
StoreBuffer.h \
|
||||
$(NULL)
|
||||
|
||||
######################################################
|
||||
|
|
|
@ -247,25 +247,13 @@ SelectForGC(JSContext *cx, unsigned argc, jsval *vp)
|
|||
}
|
||||
|
||||
static JSBool
|
||||
VerifyPreBarriers(JSContext *cx, unsigned argc, jsval *vp)
|
||||
VerifyBarriers(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc) {
|
||||
ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Too many arguments");
|
||||
return JS_FALSE;
|
||||
}
|
||||
gc::VerifyBarriers(cx->runtime, gc::PreBarrierVerifier);
|
||||
*vp = JSVAL_VOID;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
VerifyPostBarriers(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc) {
|
||||
ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Too many arguments");
|
||||
return JS_FALSE;
|
||||
}
|
||||
gc::VerifyBarriers(cx->runtime, gc::PostBarrierVerifier);
|
||||
gc::VerifyBarriers(cx->runtime);
|
||||
*vp = JSVAL_VOID;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -609,15 +597,10 @@ static JSFunctionSpecWithHelp TestingFunctions[] = {
|
|||
" 1: Collect when roots are added or removed\n"
|
||||
" 2: Collect when memory is allocated\n"
|
||||
" 3: Collect when the window paints (browser only)\n"
|
||||
" 4: Verify pre write barriers between instructions\n"
|
||||
" 5: Verify pre write barriers between paints\n"
|
||||
" 4: Verify write barriers between instructions\n"
|
||||
" 5: Verify write barriers between paints\n"
|
||||
" 6: Verify stack rooting (ignoring XML and Reflect)\n"
|
||||
" 7: Verify stack rooting (all roots)\n"
|
||||
" 8: Incremental GC in two slices: 1) mark roots 2) finish collection\n"
|
||||
" 9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
|
||||
" 10: Incremental GC in multiple slices\n"
|
||||
" 11: Verify post write barriers between instructions\n"
|
||||
" 12: Verify post write barriers between paints\n"
|
||||
" Period specifies that collection happens every n allocations.\n"),
|
||||
|
||||
JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
|
||||
|
@ -629,13 +612,9 @@ static JSFunctionSpecWithHelp TestingFunctions[] = {
|
|||
"selectforgc(obj1, obj2, ...)",
|
||||
" Schedule the given objects to be marked in the next GC slice."),
|
||||
|
||||
JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
|
||||
"verifyprebarriers()",
|
||||
" Start or end a run of the pre-write barrier verifier."),
|
||||
|
||||
JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0,
|
||||
"verifypostbarriers()",
|
||||
" Start or end a run of the post-write barrier verifier."),
|
||||
JS_FN_HELP("verifybarriers", VerifyBarriers, 0, 0,
|
||||
"verifybarriers()",
|
||||
" Start or end a run of the write barrier verifier."),
|
||||
|
||||
JS_FN_HELP("gcslice", GCSlice, 1, 0,
|
||||
"gcslice(n)",
|
||||
|
|
|
@ -296,7 +296,7 @@ HeapSlot::writeBarrierPost(JSObject *obj, uint32_t slot)
|
|||
}
|
||||
|
||||
inline void
|
||||
HeapSlot::writeBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t slot)
|
||||
HeapSlot::writeBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t slotno)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -414,7 +414,7 @@ class HeapSlot : public EncapsulatedValue
|
|||
inline void set(JSCompartment *comp, JSObject *owner, uint32_t slot, const Value &v);
|
||||
|
||||
static inline void writeBarrierPost(JSObject *obj, uint32_t slot);
|
||||
static inline void writeBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t slot);
|
||||
static inline void writeBarrierPost(JSCompartment *comp, JSObject *obj, uint32_t slotno);
|
||||
|
||||
private:
|
||||
inline void post(JSObject *owner, uint32_t slot);
|
||||
|
|
|
@ -1,271 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=78:
|
||||
*/
|
||||
/* 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/ObjectImpl-inl.h"
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
/*** MonoTypeBuffer ***/
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
StoreBuffer::MonoTypeBuffer<T>::disable()
|
||||
{
|
||||
base = pos = top = NULL;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
StoreBuffer::MonoTypeBuffer<T>::compactNotInSet()
|
||||
{
|
||||
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>::compact()
|
||||
{
|
||||
compactNotInSet();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
StoreBuffer::MonoTypeBuffer<T>::put(const T &v)
|
||||
{
|
||||
/* Check if we have been enabled. */
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Note: it is sometimes valid for a put to happen in the middle of a GC,
|
||||
* e.g. a rekey of a Relocatable may end up here. In general, we do not
|
||||
* care about these new entries or any overflows they cause.
|
||||
*/
|
||||
*pos++ = v;
|
||||
if (isFull()) {
|
||||
owner->setOverflowed();
|
||||
pos = base;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
StoreBuffer::MonoTypeBuffer<T>::accumulateEdges(EdgeSet &edges)
|
||||
{
|
||||
compact();
|
||||
T *cursor = base;
|
||||
while (cursor != pos) {
|
||||
T edge = *cursor++;
|
||||
|
||||
/* Note: the relocatable buffer is allowed to store pointers to NULL. */
|
||||
if (edge.isNullEdge())
|
||||
continue;
|
||||
if (!edges.putNew(edge.location()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*** 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();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
StoreBuffer::RelocatableMonoTypeBuffer<T>::unput(const T &v)
|
||||
{
|
||||
put(v.tagged());
|
||||
}
|
||||
|
||||
/*** 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;
|
||||
}
|
||||
|
||||
bool
|
||||
StoreBuffer::GenericBuffer::containsEdge(void *location) const
|
||||
{
|
||||
uint8_t *p = base;
|
||||
while (p < pos) {
|
||||
unsigned size = *((unsigned *)p);
|
||||
p += sizeof(unsigned);
|
||||
|
||||
if (((BufferableRef *)p)->match(location))
|
||||
return true;
|
||||
|
||||
p += size;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*** StoreBuffer ***/
|
||||
|
||||
bool
|
||||
StoreBuffer::enable()
|
||||
{
|
||||
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 (!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()
|
||||
{
|
||||
bufferVal.disable();
|
||||
bufferCell.disable();
|
||||
bufferSlot.disable();
|
||||
bufferRelocVal.disable();
|
||||
bufferRelocCell.disable();
|
||||
bufferGeneric.disable();
|
||||
|
||||
js_free(buffer);
|
||||
enabled = false;
|
||||
overflowed = false;
|
||||
}
|
||||
|
||||
bool
|
||||
StoreBuffer::coalesceForVerification()
|
||||
{
|
||||
if (!edgeSet.initialized()) {
|
||||
if (!edgeSet.init())
|
||||
return false;
|
||||
}
|
||||
JS_ASSERT(edgeSet.empty());
|
||||
if (!bufferVal.accumulateEdges(edgeSet))
|
||||
return false;
|
||||
if (!bufferCell.accumulateEdges(edgeSet))
|
||||
return false;
|
||||
if (!bufferSlot.accumulateEdges(edgeSet))
|
||||
return false;
|
||||
if (!bufferRelocVal.accumulateEdges(edgeSet))
|
||||
return false;
|
||||
if (!bufferRelocCell.accumulateEdges(edgeSet))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StoreBuffer::containsEdgeAt(void *loc) const
|
||||
{
|
||||
return edgeSet.has(loc) || bufferGeneric.containsEdge(loc);
|
||||
}
|
||||
|
||||
void
|
||||
StoreBuffer::releaseVerificationData()
|
||||
{
|
||||
edgeSet.finish();
|
||||
}
|
||||
|
||||
template class StoreBuffer::MonoTypeBuffer<StoreBuffer::ValueEdge>;
|
||||
template class StoreBuffer::MonoTypeBuffer<StoreBuffer::CellPtrEdge>;
|
||||
template class StoreBuffer::MonoTypeBuffer<StoreBuffer::SlotEdge>;
|
||||
template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::ValueEdge>;
|
||||
template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::CellPtrEdge>;
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* JSGC_GENERATIONAL */
|
|
@ -1,397 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=78:
|
||||
*
|
||||
* 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
|
||||
#ifndef jsgc_storebuffer_h___
|
||||
#define jsgc_storebuffer_h___
|
||||
|
||||
#include "jsgc.h"
|
||||
#include "jsalloc.h"
|
||||
|
||||
#include "gc/Marking.h"
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
/*
|
||||
* Note: this is a stub Nursery that does not actually contain a heap, just a
|
||||
* set of pointers which are "inside" the nursery to implement verification.
|
||||
*/
|
||||
class Nursery
|
||||
{
|
||||
HashSet<void*, PointerHasher<void*, 3>, SystemAllocPolicy> nursery;
|
||||
|
||||
public:
|
||||
Nursery() : nursery() {}
|
||||
|
||||
bool enable() {
|
||||
if (!nursery.initialized())
|
||||
return nursery.init();
|
||||
return true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
if (!nursery.initialized())
|
||||
return;
|
||||
nursery.finish();
|
||||
}
|
||||
|
||||
bool isInside(void *cell) const {
|
||||
return nursery.initialized() && nursery.has(cell);
|
||||
}
|
||||
|
||||
void insertPointer(void *cell) {
|
||||
nursery.putNew(cell);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* BufferableRef represents an abstract reference for use in the generational
|
||||
* GC's remembered set. Entries in the store buffer that cannot be represented
|
||||
* with the simple pointer-to-a-pointer scheme must derive from this class and
|
||||
* use the generic store buffer interface.
|
||||
*/
|
||||
class BufferableRef
|
||||
{
|
||||
public:
|
||||
virtual bool match(void *location) = 0;
|
||||
virtual void mark(JSTracer *trc) = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* HashKeyRef represents a reference to a HashTable key. Manual HashTable
|
||||
* barriers should should instantiate this template with their own table/key
|
||||
* type to insert into the generic buffer with putGeneric.
|
||||
*/
|
||||
template <typename Map, typename Key>
|
||||
class HashKeyRef : public BufferableRef
|
||||
{
|
||||
Map *map;
|
||||
Key key;
|
||||
|
||||
typedef typename Map::Ptr Ptr;
|
||||
|
||||
public:
|
||||
HashKeyRef(Map *m, const Key &k) : map(m), key(k) {}
|
||||
|
||||
bool match(void *location) {
|
||||
Ptr p = map->lookup(key);
|
||||
if (!p)
|
||||
return false;
|
||||
return &p->key == location;
|
||||
}
|
||||
|
||||
void mark(JSTracer *trc) {}
|
||||
};
|
||||
|
||||
/*
|
||||
* The StoreBuffer observes all writes that occur in the system and performs
|
||||
* efficient filtering of them to derive a remembered set for nursery GC.
|
||||
*/
|
||||
class StoreBuffer
|
||||
{
|
||||
/* TODO: profile to find the ideal size for these. */
|
||||
static const size_t ValueBufferSize = 1 * 1024 * sizeof(Value *);
|
||||
static const size_t CellBufferSize = 2 * 1024 * sizeof(Cell **);
|
||||
static const size_t SlotBufferSize = 2 * 1024 * (sizeof(JSObject *) + sizeof(uint32_t));
|
||||
static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(Value *);
|
||||
static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(Cell **);
|
||||
static const size_t GenericBufferSize = 1 * 1024 * sizeof(int);
|
||||
static const size_t TotalSize = ValueBufferSize + CellBufferSize +
|
||||
SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize +
|
||||
GenericBufferSize;
|
||||
|
||||
typedef HashSet<void *, PointerHasher<void *, 3>, SystemAllocPolicy> EdgeSet;
|
||||
|
||||
/*
|
||||
* This buffer holds only a single type of edge. Using this buffer is more
|
||||
* efficient than the generic buffer when many writes will be to the same
|
||||
* type of edge: e.g. Value or Cell*.
|
||||
*/
|
||||
template<typename T>
|
||||
class MonoTypeBuffer
|
||||
{
|
||||
friend class StoreBuffer;
|
||||
|
||||
StoreBuffer *owner;
|
||||
Nursery *nursery;
|
||||
|
||||
T *base; /* Pointer to the start of the buffer. */
|
||||
T *pos; /* Pointer to the current insertion position. */
|
||||
T *top; /* Pointer to one element after the end. */
|
||||
|
||||
MonoTypeBuffer(StoreBuffer *owner, Nursery *nursery)
|
||||
: owner(owner), nursery(nursery), base(NULL), pos(NULL), top(NULL)
|
||||
{}
|
||||
|
||||
MonoTypeBuffer &operator=(const MonoTypeBuffer& other) MOZ_DELETE;
|
||||
|
||||
bool enable(uint8_t *region, size_t len);
|
||||
void disable();
|
||||
|
||||
bool isEmpty() const { return pos == base; }
|
||||
bool isFull() const { JS_ASSERT(pos <= top); return pos == top; }
|
||||
|
||||
/* Compaction algorithms. */
|
||||
void compactNotInSet();
|
||||
|
||||
/*
|
||||
* Attempts to reduce the usage of the buffer by removing unnecessary
|
||||
* entries.
|
||||
*/
|
||||
void compact();
|
||||
|
||||
/* Add one item to the buffer. */
|
||||
void put(const T &v);
|
||||
|
||||
/* For verification. */
|
||||
bool accumulateEdges(EdgeSet &edges);
|
||||
};
|
||||
|
||||
/*
|
||||
* Overrides the MonoTypeBuffer to support pointers that may be moved in
|
||||
* memory outside of the GC's control.
|
||||
*/
|
||||
template <typename T>
|
||||
class RelocatableMonoTypeBuffer : public MonoTypeBuffer<T>
|
||||
{
|
||||
friend class StoreBuffer;
|
||||
|
||||
RelocatableMonoTypeBuffer(StoreBuffer *owner, Nursery *nursery)
|
||||
: MonoTypeBuffer<T>(owner, nursery)
|
||||
{}
|
||||
|
||||
/* Override compaction to filter out removed items. */
|
||||
void compactMoved();
|
||||
void compact();
|
||||
|
||||
/* Record a removal from the buffer. */
|
||||
void unput(const T &v);
|
||||
};
|
||||
|
||||
class GenericBuffer
|
||||
{
|
||||
friend class StoreBuffer;
|
||||
|
||||
StoreBuffer *owner;
|
||||
Nursery *nursery;
|
||||
|
||||
uint8_t *base; /* Pointer to start of buffer. */
|
||||
uint8_t *pos; /* Pointer to current buffer position. */
|
||||
uint8_t *top; /* Pointer to one past the last entry. */
|
||||
|
||||
GenericBuffer(StoreBuffer *owner, Nursery *nursery)
|
||||
: owner(owner), nursery(nursery)
|
||||
{}
|
||||
|
||||
GenericBuffer &operator=(const GenericBuffer& other) MOZ_DELETE;
|
||||
|
||||
bool enable(uint8_t *region, size_t len);
|
||||
void disable();
|
||||
|
||||
/* Check if a pointer is present in the buffer. */
|
||||
bool containsEdge(void *location) const;
|
||||
|
||||
template <typename T>
|
||||
void put(const T &t) {
|
||||
/* Check if we have been enabled. */
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
/* Check for overflow. */
|
||||
if (top - pos < (unsigned)(sizeof(unsigned) + sizeof(T))) {
|
||||
owner->setOverflowed();
|
||||
return;
|
||||
}
|
||||
|
||||
*((unsigned *)pos) = sizeof(T);
|
||||
pos += sizeof(unsigned);
|
||||
|
||||
T *p = (T *)pos;
|
||||
new (p) T(t);
|
||||
pos += sizeof(T);
|
||||
}
|
||||
};
|
||||
|
||||
class CellPtrEdge
|
||||
{
|
||||
friend class StoreBuffer;
|
||||
friend class StoreBuffer::MonoTypeBuffer<CellPtrEdge>;
|
||||
friend class StoreBuffer::RelocatableMonoTypeBuffer<CellPtrEdge>;
|
||||
|
||||
Cell **edge;
|
||||
|
||||
CellPtrEdge(Cell **v) : edge(v) {}
|
||||
bool operator==(const CellPtrEdge &other) const { return edge == other.edge; }
|
||||
bool operator!=(const CellPtrEdge &other) const { return edge != other.edge; }
|
||||
|
||||
void *location() const { return (void *)edge; }
|
||||
|
||||
bool inRememberedSet(Nursery *n) {
|
||||
return !n->isInside(edge) && n->isInside(*edge);
|
||||
}
|
||||
|
||||
bool isNullEdge() const {
|
||||
return !*edge;
|
||||
}
|
||||
|
||||
CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); }
|
||||
CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); }
|
||||
bool isTagged() const { return bool(uintptr_t(edge) & 1); }
|
||||
};
|
||||
|
||||
class ValueEdge
|
||||
{
|
||||
friend class StoreBuffer;
|
||||
friend class StoreBuffer::MonoTypeBuffer<ValueEdge>;
|
||||
friend class StoreBuffer::RelocatableMonoTypeBuffer<ValueEdge>;
|
||||
|
||||
Value *edge;
|
||||
|
||||
ValueEdge(Value *v) : edge(v) {}
|
||||
bool operator==(const ValueEdge &other) const { return edge == other.edge; }
|
||||
bool operator!=(const ValueEdge &other) const { return edge != other.edge; }
|
||||
|
||||
void *deref() const { return edge->isGCThing() ? edge->toGCThing() : NULL; }
|
||||
void *location() const { return (void *)edge; }
|
||||
|
||||
bool inRememberedSet(Nursery *n) {
|
||||
return !n->isInside(edge) && n->isInside(deref());
|
||||
}
|
||||
|
||||
bool isNullEdge() const {
|
||||
return !deref();
|
||||
}
|
||||
|
||||
ValueEdge tagged() const { return ValueEdge((Value *)(uintptr_t(edge) | 1)); }
|
||||
ValueEdge untagged() const { return ValueEdge((Value *)(uintptr_t(edge) & ~1)); }
|
||||
bool isTagged() const { return bool(uintptr_t(edge) & 1); }
|
||||
};
|
||||
|
||||
struct SlotEdge
|
||||
{
|
||||
friend class StoreBuffer;
|
||||
friend class StoreBuffer::MonoTypeBuffer<SlotEdge>;
|
||||
|
||||
JSObject *object;
|
||||
uint32_t offset;
|
||||
|
||||
SlotEdge(JSObject *object, uint32_t offset) : object(object), offset(offset) {}
|
||||
|
||||
bool operator==(const SlotEdge &other) const {
|
||||
return object == other.object && offset == other.offset;
|
||||
}
|
||||
|
||||
bool operator!=(const SlotEdge &other) const {
|
||||
return object != other.object || offset != other.offset;
|
||||
}
|
||||
|
||||
HeapSlot *slotLocation() const {
|
||||
if (object->isDenseArray()) {
|
||||
if (offset >= object->getDenseArrayInitializedLength())
|
||||
return NULL;
|
||||
return (HeapSlot *)&object->getDenseArrayElement(offset);
|
||||
}
|
||||
if (offset >= object->slotSpan())
|
||||
return NULL;
|
||||
return &object->getSlotRef(offset);
|
||||
}
|
||||
|
||||
void *deref() const {
|
||||
HeapSlot *loc = slotLocation();
|
||||
return (loc && loc->isGCThing()) ? loc->toGCThing() : NULL;
|
||||
}
|
||||
|
||||
void *location() const {
|
||||
return (void *)slotLocation();
|
||||
}
|
||||
|
||||
bool inRememberedSet(Nursery *n) {
|
||||
return !n->isInside(object) && n->isInside(deref());
|
||||
}
|
||||
|
||||
bool isNullEdge() const {
|
||||
return !deref();
|
||||
}
|
||||
};
|
||||
|
||||
MonoTypeBuffer<ValueEdge> bufferVal;
|
||||
MonoTypeBuffer<CellPtrEdge> bufferCell;
|
||||
MonoTypeBuffer<SlotEdge> bufferSlot;
|
||||
RelocatableMonoTypeBuffer<ValueEdge> bufferRelocVal;
|
||||
RelocatableMonoTypeBuffer<CellPtrEdge> bufferRelocCell;
|
||||
GenericBuffer bufferGeneric;
|
||||
|
||||
Nursery *nursery;
|
||||
|
||||
void *buffer;
|
||||
|
||||
bool overflowed;
|
||||
bool enabled;
|
||||
|
||||
/* For the verifier. */
|
||||
EdgeSet edgeSet;
|
||||
|
||||
/* For use by our owned buffers. */
|
||||
void setOverflowed() { overflowed = true; }
|
||||
|
||||
public:
|
||||
StoreBuffer(Nursery *n)
|
||||
: bufferVal(this, n), bufferCell(this, n), bufferSlot(this, n),
|
||||
bufferRelocVal(this, n), bufferRelocCell(this, n), bufferGeneric(this, n),
|
||||
nursery(n), buffer(NULL), overflowed(false), enabled(false)
|
||||
{}
|
||||
|
||||
bool enable();
|
||||
void disable();
|
||||
bool isEnabled() { return enabled; }
|
||||
|
||||
/* Get the overflowed status. */
|
||||
bool hasOverflowed() const { return overflowed; }
|
||||
|
||||
/* Insert a single edge into the buffer/remembered set. */
|
||||
void putValue(Value *v) {
|
||||
bufferVal.put(v);
|
||||
}
|
||||
void putCell(Cell **o) {
|
||||
bufferCell.put(o);
|
||||
}
|
||||
void putSlot(JSObject *obj, uint32_t slot) {
|
||||
bufferSlot.put(SlotEdge(obj, slot));
|
||||
}
|
||||
|
||||
/* Insert or update a single edge in the Relocatable buffer. */
|
||||
void putRelocatableValue(Value *v) {
|
||||
bufferRelocVal.put(v);
|
||||
}
|
||||
void putRelocatableCell(Cell **c) {
|
||||
bufferRelocCell.put(c);
|
||||
}
|
||||
void removeRelocatableValue(Value *v) {
|
||||
bufferRelocVal.unput(v);
|
||||
}
|
||||
void removeRelocatableCell(Cell **c) {
|
||||
bufferRelocCell.unput(c);
|
||||
}
|
||||
|
||||
/* Insert an entry into the generic buffer. */
|
||||
template <typename T>
|
||||
void putGeneric(const T &t) {
|
||||
bufferGeneric.put(t);
|
||||
}
|
||||
|
||||
/* For the verifier. */
|
||||
bool coalesceForVerification();
|
||||
void releaseVerificationData();
|
||||
bool containsEdgeAt(void *loc) const;
|
||||
};
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jsgc_storebuffer_h___ */
|
||||
#endif /* JSGC_GENERATIONAL */
|
|
@ -24,12 +24,8 @@ if (!("selectforgc" in this)) {
|
|||
selectforgc = function() { }
|
||||
}
|
||||
|
||||
if (!("verifyprebarriers" in this)) {
|
||||
verifyprebarriers = function() { }
|
||||
}
|
||||
|
||||
if (!("verifypostbarriers" in this)) {
|
||||
verifypostbarriers = function() { }
|
||||
if (!("verifybarriers" in this)) {
|
||||
verifybarriers = function() { }
|
||||
}
|
||||
|
||||
if ("options" in this)
|
||||
|
|
|
@ -17,4 +17,4 @@ function h(code, globalType) {
|
|||
}
|
||||
}
|
||||
function p()(function() function() {})
|
||||
g("print(let(x=verifyprebarriers(),q)((x(\"\",l('')))?(\"\"):(\"\")))()")
|
||||
g("print(let(x=verifybarriers(),q)((x(\"\",l('')))?(\"\"):(\"\")))()")
|
||||
|
|
|
@ -10,9 +10,9 @@ function f2(o)
|
|||
function f()
|
||||
{
|
||||
var x = new ctor();
|
||||
verifyprebarriers();
|
||||
verifybarriers();
|
||||
f2(x);
|
||||
verifyprebarriers();
|
||||
verifybarriers();
|
||||
}
|
||||
f();
|
||||
f();
|
||||
|
|
|
@ -747,8 +747,7 @@ JSRuntime::JSRuntime()
|
|||
gcMaxBytes(0),
|
||||
gcMaxMallocBytes(0),
|
||||
gcNumArenasFreeCommitted(0),
|
||||
gcVerifyPreData(NULL),
|
||||
gcVerifyPostData(NULL),
|
||||
gcVerifyData(NULL),
|
||||
gcChunkAllocationSinceLastGC(false),
|
||||
gcNextFullGCTime(0),
|
||||
gcLastGCTime(0),
|
||||
|
@ -6749,27 +6748,21 @@ JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency)
|
|||
" 1: additional GCs at common danger points\n"
|
||||
" 2: GC every F allocations (default: 100)\n"
|
||||
" 3: GC when the window paints (browser only)\n"
|
||||
" 4: Verify pre write barriers between instructions\n"
|
||||
" 5: Verify pre write barriers between paints\n"
|
||||
" 4: Verify write barriers between instructions\n"
|
||||
" 5: Verify write barriers between paints\n"
|
||||
" 6: Verify stack rooting (ignoring XML and Reflect)\n"
|
||||
" 7: Verify stack rooting (all roots)\n"
|
||||
" 8: Incremental GC in two slices: 1) mark roots 2) finish collection\n"
|
||||
" 9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
|
||||
" 10: Incremental GC in multiple slices\n"
|
||||
" 11: Verify post write barriers between instructions\n"
|
||||
" 12: Verify post write barriers between paints\n");
|
||||
" 10: Incremental GC in multiple slices\n");
|
||||
}
|
||||
const char *p = strchr(env, ',');
|
||||
zeal = atoi(env);
|
||||
frequency = p ? atoi(p + 1) : JS_DEFAULT_ZEAL_FREQ;
|
||||
}
|
||||
|
||||
if (zeal == 0) {
|
||||
if (cx->runtime->gcVerifyPreData)
|
||||
VerifyBarriers(cx->runtime, PreBarrierVerifier);
|
||||
if (cx->runtime->gcVerifyPostData)
|
||||
VerifyBarriers(cx->runtime, PostBarrierVerifier);
|
||||
}
|
||||
if (zeal == 0 && cx->runtime->gcVerifyData)
|
||||
VerifyBarriers(cx->runtime);
|
||||
|
||||
bool schedule = zeal >= js::gc::ZealAllocValue;
|
||||
cx->runtime->gcZeal_ = zeal;
|
||||
|
|
|
@ -859,7 +859,6 @@ class ValueOperations
|
|||
bool isMagic(JSWhyMagic why) const { return value()->isMagic(why); }
|
||||
bool isMarkable() const { return value()->isMarkable(); }
|
||||
bool isPrimitive() const { return value()->isPrimitive(); }
|
||||
bool isGCThing() const { return value()->isGCThing(); }
|
||||
|
||||
bool toBoolean() const { return value()->toBoolean(); }
|
||||
double toNumber() const { return value()->toNumber(); }
|
||||
|
@ -3668,7 +3667,7 @@ struct JSTracer {
|
|||
const void *debugPrintArg;
|
||||
size_t debugPrintIndex;
|
||||
JSBool eagerlyTraceWeakMaps;
|
||||
#ifdef JS_GC_ZEAL
|
||||
#ifdef DEBUG
|
||||
void *realLocation;
|
||||
#endif
|
||||
};
|
||||
|
@ -3708,16 +3707,11 @@ JS_CallTracer(JSTracer *trc, void *thing, JSGCTraceKind kind);
|
|||
/*
|
||||
* Sets the real location for a marked reference, when passing the address
|
||||
* directly is not feasable.
|
||||
*
|
||||
* FIXME: This is currently overcomplicated by our need to nest calls for Values
|
||||
* stored as keys in hash tables, but will get simplified once we can rekey
|
||||
* in-place.
|
||||
*/
|
||||
#ifdef JS_GC_ZEAL
|
||||
#ifdef DEBUG
|
||||
# define JS_SET_TRACING_LOCATION(trc, location) \
|
||||
JS_BEGIN_MACRO \
|
||||
if ((trc)->realLocation == NULL || (location) == NULL) \
|
||||
(trc)->realLocation = (location); \
|
||||
(trc)->realLocation = (location); \
|
||||
JS_END_MACRO
|
||||
#else
|
||||
# define JS_SET_TRACING_LOCATION(trc, location) \
|
||||
|
|
|
@ -502,8 +502,7 @@ struct JSRuntime : js::RuntimeFriendFields
|
|||
*/
|
||||
volatile uint32_t gcNumArenasFreeCommitted;
|
||||
js::GCMarker gcMarker;
|
||||
void *gcVerifyPreData;
|
||||
void *gcVerifyPostData;
|
||||
void *gcVerifyData;
|
||||
bool gcChunkAllocationSinceLastGC;
|
||||
int64_t gcNextFullGCTime;
|
||||
int64_t gcLastGCTime;
|
||||
|
|
|
@ -41,9 +41,6 @@ JSCompartment::JSCompartment(JSRuntime *rt)
|
|||
: rt(rt),
|
||||
principals(NULL),
|
||||
global_(NULL),
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
gcStoreBuffer(&gcNursery),
|
||||
#endif
|
||||
needsBarrier_(false),
|
||||
gcState(NoGCScheduled),
|
||||
gcPreserveCode(false),
|
||||
|
@ -92,20 +89,6 @@ JSCompartment::init(JSContext *cx)
|
|||
if (!regExps.init(cx))
|
||||
return false;
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
/*
|
||||
* If we are in the middle of post-barrier verification, we need to
|
||||
* immediately begin collecting verification data on new compartments.
|
||||
*/
|
||||
if (rt->gcVerifyPostData) {
|
||||
if (!gcNursery.enable())
|
||||
return false;
|
||||
|
||||
if (!gcStoreBuffer.enable())
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return debuggees.init();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "jsobj.h"
|
||||
#include "jsscope.h"
|
||||
|
||||
#include "gc/StoreBuffer.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
|
||||
|
@ -145,11 +144,6 @@ struct JSCompartment
|
|||
public:
|
||||
js::gc::ArenaLists arenas;
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
js::gc::Nursery gcNursery;
|
||||
js::gc::StoreBuffer gcStoreBuffer;
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool needsBarrier_;
|
||||
public:
|
||||
|
|
|
@ -776,13 +776,8 @@ GCDescription::formatJSON(JSRuntime *rt, uint64_t timestamp) const
|
|||
JS_FRIEND_API(void)
|
||||
NotifyDidPaint(JSRuntime *rt)
|
||||
{
|
||||
if (rt->gcZeal() == gc::ZealFrameVerifierPreValue) {
|
||||
gc::VerifyBarriers(rt, gc::PreBarrierVerifier);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rt->gcZeal() == gc::ZealFrameVerifierPostValue) {
|
||||
gc::VerifyBarriers(rt, gc::PostBarrierVerifier);
|
||||
if (rt->gcZeal() == gc::ZealFrameVerifierValue) {
|
||||
gc::VerifyBarriers(rt);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
360
js/src/jsgc.cpp
360
js/src/jsgc.cpp
|
@ -120,16 +120,10 @@ const int IGC_MARK_SLICE_MULTIPLIER = 2;
|
|||
|
||||
#ifdef JS_GC_ZEAL
|
||||
static void
|
||||
StartVerifyPreBarriers(JSRuntime *rt);
|
||||
StartVerifyBarriers(JSRuntime *rt);
|
||||
|
||||
static void
|
||||
EndVerifyPreBarriers(JSRuntime *rt);
|
||||
|
||||
static void
|
||||
StartVerifyPostBarriers(JSRuntime *rt);
|
||||
|
||||
static void
|
||||
EndVerifyPostBarriers(JSRuntime *rt);
|
||||
EndVerifyBarriers(JSRuntime *rt);
|
||||
|
||||
void
|
||||
FinishVerifier(JSRuntime *rt);
|
||||
|
@ -898,11 +892,11 @@ MarkExactStackRoots(JSTracer *trc)
|
|||
} else if (i == THING_ROOT_ID) {
|
||||
MarkIdRoot(trc, (jsid *)addr, "exact stackroot id");
|
||||
} else if (i == THING_ROOT_PROPERTY_ID) {
|
||||
MarkIdRoot(trc, &((PropertyId *)addr)->asId(), "exact stackroot property id");
|
||||
MarkIdRoot(trc, ((PropertyId *)addr)->asId(), "exact stackroot property id");
|
||||
} else if (i == THING_ROOT_VALUE) {
|
||||
MarkValueRoot(trc, (Value *)addr, "exact stackroot value");
|
||||
} else if (i == THING_ROOT_TYPE) {
|
||||
MarkTypeRoot(trc, (types::Type *)addr, "exact stackroot type");
|
||||
MarkTypeRoot(trc, (Type)addr, "exact stackroot type");
|
||||
} else if (i == THING_ROOT_SHAPE) {
|
||||
MarkShapeRoot(trc, (Shape **)addr, "exact stackroot shape");
|
||||
} else if (i == THING_ROOT_BASE_SHAPE) {
|
||||
|
@ -1399,6 +1393,7 @@ JSCompartment::setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, JSGCInvo
|
|||
+ rt->gcHighFrequencyHeapGrowthMax);
|
||||
JS_ASSERT(gcHeapGrowthFactor <= rt->gcHighFrequencyHeapGrowthMax
|
||||
&& gcHeapGrowthFactor >= rt->gcHighFrequencyHeapGrowthMin);
|
||||
|
||||
}
|
||||
rt->gcHighFrequencyGC = true;
|
||||
} else {
|
||||
|
@ -1807,9 +1802,6 @@ InitTracer(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback)
|
|||
trc->debugPrintArg = NULL;
|
||||
trc->debugPrintIndex = size_t(-1);
|
||||
trc->eagerlyTraceWeakMaps = true;
|
||||
#ifdef JS_GC_ZEAL
|
||||
trc->realLocation = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* static */ int64_t
|
||||
|
@ -3948,28 +3940,25 @@ Collect(JSRuntime *rt, bool incremental, int64_t budget,
|
|||
JS_ASSERT_IF(!incremental || budget != SliceBudget::Unlimited, JSGC_INCREMENTAL);
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
bool isShutdown = reason == gcreason::SHUTDOWN_CC || !rt->hasContexts();
|
||||
bool restartVerify = rt->gcVerifyData &&
|
||||
rt->gcZeal() == ZealVerifierValue &&
|
||||
reason != gcreason::SHUTDOWN_CC &&
|
||||
rt->hasContexts();
|
||||
|
||||
struct AutoVerifyBarriers {
|
||||
JSRuntime *runtime;
|
||||
bool restartPreVerifier;
|
||||
bool restartPostVerifier;
|
||||
AutoVerifyBarriers(JSRuntime *rt, bool isShutdown)
|
||||
: runtime(rt)
|
||||
bool restart;
|
||||
AutoVerifyBarriers(JSRuntime *rt, bool restart)
|
||||
: runtime(rt), restart(restart)
|
||||
{
|
||||
restartPreVerifier = !isShutdown && rt->gcVerifyPreData;
|
||||
restartPostVerifier = !isShutdown && rt->gcVerifyPostData;
|
||||
if (rt->gcVerifyPreData)
|
||||
EndVerifyPreBarriers(rt);
|
||||
if (rt->gcVerifyPostData)
|
||||
EndVerifyPostBarriers(rt);
|
||||
if (rt->gcVerifyData)
|
||||
EndVerifyBarriers(rt);
|
||||
}
|
||||
~AutoVerifyBarriers() {
|
||||
if (restartPreVerifier)
|
||||
StartVerifyPreBarriers(runtime);
|
||||
if (restartPostVerifier)
|
||||
StartVerifyPostBarriers(runtime);
|
||||
if (restart)
|
||||
StartVerifyBarriers(runtime);
|
||||
}
|
||||
} av(rt, isShutdown);
|
||||
} av(rt, restartVerify);
|
||||
#endif
|
||||
|
||||
RecordNativeStackTopForGC(rt);
|
||||
|
@ -4456,7 +4445,12 @@ namespace gc {
|
|||
/*
|
||||
* Write barrier verification
|
||||
*
|
||||
* The next few functions are for write barrier verification.
|
||||
* The next few functions are for incremental write barrier verification. When
|
||||
* StartVerifyBarriers is called, a snapshot is taken of all objects in the GC
|
||||
* heap and saved in an explicit graph data structure. Later, EndVerifyBarriers
|
||||
* traverses the heap again. Any pointer values that were in the snapshot and
|
||||
* are no longer found must be marked; otherwise an assertion triggers. Note
|
||||
* that we must not GC in between starting and finishing a verification phase.
|
||||
*
|
||||
* The VerifyBarriers function is a shorthand. It checks if a verification phase
|
||||
* is currently running. If not, it starts one. Otherwise, it ends the current
|
||||
|
@ -4465,21 +4459,6 @@ namespace gc {
|
|||
* The user can adjust the frequency of verifications, which causes
|
||||
* VerifyBarriers to be a no-op all but one out of N calls. However, if the
|
||||
* |always| parameter is true, it starts a new phase no matter what.
|
||||
*
|
||||
* Pre-Barrier Verifier:
|
||||
* When StartVerifyBarriers is called, a snapshot is taken of all objects in
|
||||
* the GC heap and saved in an explicit graph data structure. Later,
|
||||
* EndVerifyBarriers traverses the heap again. Any pointer values that were in
|
||||
* the snapshot and are no longer found must be marked; otherwise an assertion
|
||||
* triggers. Note that we must not GC in between starting and finishing a
|
||||
* verification phase.
|
||||
*
|
||||
* Post-Barrier Verifier:
|
||||
* When StartVerifyBarriers is called, we create a virtual "Nursery Set" which
|
||||
* future allocations are recorded in and turn on the StoreBuffer. Later,
|
||||
* EndVerifyBarriers traverses the heap and ensures that the set of cross-
|
||||
* generational pointers we find is a subset of the pointers recorded in our
|
||||
* StoreBuffer.
|
||||
*/
|
||||
|
||||
struct EdgeValue
|
||||
|
@ -4512,12 +4491,12 @@ typedef HashMap<void *, VerifyNode *, DefaultHasher<void *>, SystemAllocPolicy>
|
|||
* The nodemap field is a hashtable that maps from the address of the GC thing
|
||||
* to the VerifyNode that represents it.
|
||||
*/
|
||||
struct VerifyPreTracer : JSTracer {
|
||||
struct VerifyTracer : JSTracer {
|
||||
/* The gcNumber when the verification began. */
|
||||
uint64_t number;
|
||||
|
||||
/* This counts up to gcZealFrequency to decide whether to verify. */
|
||||
int count;
|
||||
/* This counts up to JS_VERIFIER_FREQ to decide whether to verify. */
|
||||
uint32_t count;
|
||||
|
||||
/* This graph represents the initial GC "snapshot". */
|
||||
VerifyNode *curnode;
|
||||
|
@ -4526,8 +4505,8 @@ struct VerifyPreTracer : JSTracer {
|
|||
char *term;
|
||||
NodeMap nodemap;
|
||||
|
||||
VerifyPreTracer() : root(NULL) {}
|
||||
~VerifyPreTracer() { js_free(root); }
|
||||
VerifyTracer() : root(NULL) {}
|
||||
~VerifyTracer() { js_free(root); }
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -4537,7 +4516,7 @@ struct VerifyPreTracer : JSTracer {
|
|||
static void
|
||||
AccumulateEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
|
||||
{
|
||||
VerifyPreTracer *trc = (VerifyPreTracer *)jstrc;
|
||||
VerifyTracer *trc = (VerifyTracer *)jstrc;
|
||||
|
||||
trc->edgeptr += sizeof(EdgeValue);
|
||||
if (trc->edgeptr >= trc->term) {
|
||||
|
@ -4555,7 +4534,7 @@ AccumulateEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
|
|||
}
|
||||
|
||||
static VerifyNode *
|
||||
MakeNode(VerifyPreTracer *trc, void *thing, JSGCTraceKind kind)
|
||||
MakeNode(VerifyTracer *trc, void *thing, JSGCTraceKind kind)
|
||||
{
|
||||
NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing);
|
||||
if (!p) {
|
||||
|
@ -4587,9 +4566,9 @@ NextNode(VerifyNode *node)
|
|||
}
|
||||
|
||||
static void
|
||||
StartVerifyPreBarriers(JSRuntime *rt)
|
||||
StartVerifyBarriers(JSRuntime *rt)
|
||||
{
|
||||
if (rt->gcVerifyPreData || rt->gcIncrementalState != NO_INCREMENTAL)
|
||||
if (rt->gcVerifyData || rt->gcIncrementalState != NO_INCREMENTAL)
|
||||
return;
|
||||
|
||||
AutoTraceSession session(rt);
|
||||
|
@ -4605,7 +4584,7 @@ StartVerifyPreBarriers(JSRuntime *rt)
|
|||
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
||||
r.front()->bitmap.clear();
|
||||
|
||||
VerifyPreTracer *trc = new (js_malloc(sizeof(VerifyPreTracer))) VerifyPreTracer;
|
||||
VerifyTracer *trc = new (js_malloc(sizeof(VerifyTracer))) VerifyTracer;
|
||||
|
||||
rt->gcNumber++;
|
||||
trc->number = rt->gcNumber;
|
||||
|
@ -4653,7 +4632,7 @@ StartVerifyPreBarriers(JSRuntime *rt)
|
|||
node = NextNode(node);
|
||||
}
|
||||
|
||||
rt->gcVerifyPreData = trc;
|
||||
rt->gcVerifyData = trc;
|
||||
rt->gcIncrementalState = MARK;
|
||||
rt->gcMarker.start(rt);
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
|
@ -4665,7 +4644,7 @@ StartVerifyPreBarriers(JSRuntime *rt)
|
|||
|
||||
oom:
|
||||
rt->gcIncrementalState = NO_INCREMENTAL;
|
||||
trc->~VerifyPreTracer();
|
||||
trc->~VerifyTracer();
|
||||
js_free(trc);
|
||||
}
|
||||
|
||||
|
@ -4687,7 +4666,7 @@ const static uint32_t MAX_VERIFIER_EDGES = 1000;
|
|||
static void
|
||||
CheckEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
|
||||
{
|
||||
VerifyPreTracer *trc = (VerifyPreTracer *)jstrc;
|
||||
VerifyTracer *trc = (VerifyTracer *)jstrc;
|
||||
VerifyNode *node = trc->curnode;
|
||||
|
||||
/* Avoid n^2 behavior. */
|
||||
|
@ -4718,7 +4697,7 @@ AssertMarkedOrAllocated(const EdgeValue &edge)
|
|||
}
|
||||
|
||||
static void
|
||||
EndVerifyPreBarriers(JSRuntime *rt)
|
||||
EndVerifyBarriers(JSRuntime *rt)
|
||||
{
|
||||
AutoTraceSession session(rt);
|
||||
|
||||
|
@ -4727,7 +4706,7 @@ EndVerifyPreBarriers(JSRuntime *rt)
|
|||
AutoCopyFreeListToArenas copy(rt);
|
||||
RecordNativeStackTopForGC(rt);
|
||||
|
||||
VerifyPreTracer *trc = (VerifyPreTracer *)rt->gcVerifyPreData;
|
||||
VerifyTracer *trc = (VerifyTracer *)rt->gcVerifyData;
|
||||
|
||||
if (!trc)
|
||||
return;
|
||||
|
@ -4749,7 +4728,7 @@ EndVerifyPreBarriers(JSRuntime *rt)
|
|||
JS_ASSERT(trc->number == rt->gcNumber);
|
||||
rt->gcNumber++;
|
||||
|
||||
rt->gcVerifyPreData = NULL;
|
||||
rt->gcVerifyData = NULL;
|
||||
rt->gcIncrementalState = NO_INCREMENTAL;
|
||||
|
||||
if (!compartmentCreated && IsIncrementalGCSafe(rt)) {
|
||||
|
@ -4773,244 +4752,45 @@ EndVerifyPreBarriers(JSRuntime *rt)
|
|||
rt->gcMarker.reset();
|
||||
rt->gcMarker.stop();
|
||||
|
||||
trc->~VerifyPreTracer();
|
||||
trc->~VerifyTracer();
|
||||
js_free(trc);
|
||||
}
|
||||
|
||||
/*** Post-Barrier Verifyier ***/
|
||||
|
||||
struct VerifyPostTracer : JSTracer {
|
||||
/* The gcNumber when the verification began. */
|
||||
uint64_t number;
|
||||
|
||||
/* This counts up to gcZealFrequency to decide whether to verify. */
|
||||
int count;
|
||||
};
|
||||
|
||||
/*
|
||||
* The post-barrier verifier runs the full store buffer and a fake nursery when
|
||||
* running and when it stops, walks the full heap to ensure that all the
|
||||
* important edges were inserted into the storebuffer.
|
||||
*/
|
||||
static void
|
||||
StartVerifyPostBarriers(JSRuntime *rt)
|
||||
{
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
if (!rt->gcExactScanningEnabled ||
|
||||
rt->gcVerifyPostData ||
|
||||
rt->gcIncrementalState != NO_INCREMENTAL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
VerifyPostTracer *trc = new (js_malloc(sizeof(VerifyPostTracer))) VerifyPostTracer;
|
||||
rt->gcVerifyPostData = trc;
|
||||
rt->gcNumber++;
|
||||
trc->number = rt->gcNumber;
|
||||
trc->count = 0;
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
if (IsAtomsCompartment(c))
|
||||
continue;
|
||||
|
||||
if (!c->gcNursery.enable())
|
||||
goto oom;
|
||||
|
||||
if (!c->gcStoreBuffer.enable())
|
||||
goto oom;
|
||||
}
|
||||
return;
|
||||
oom:
|
||||
trc->~VerifyPostTracer();
|
||||
js_free(trc);
|
||||
rt->gcVerifyPostData = NULL;
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
c->gcNursery.disable();
|
||||
c->gcStoreBuffer.disable();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
static void
|
||||
AssertStoreBufferContainsEdge(StoreBuffer *storebuf, void *loc, void *dst)
|
||||
{
|
||||
if (storebuf->containsEdgeAt(loc))
|
||||
return;
|
||||
|
||||
char msgbuf[1024];
|
||||
JS_snprintf(msgbuf, sizeof(msgbuf), "[post-barrier verifier] Missing edge @ %p to %p",
|
||||
loc, dst);
|
||||
MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
static void
|
||||
PostVerifierVisitEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
|
||||
{
|
||||
VerifyPostTracer *trc = (VerifyPostTracer *)jstrc;
|
||||
Cell *dst = (Cell *)*thingp;
|
||||
JSCompartment *comp = dst->compartment();
|
||||
|
||||
/*
|
||||
* Note: watchpoint markAll will give us cross-compartment pointers into the
|
||||
* atoms compartment.
|
||||
*/
|
||||
if (IsAtomsCompartment(comp))
|
||||
return;
|
||||
|
||||
/* Filter out non cross-generational edges. */
|
||||
if (!comp->gcNursery.isInside(dst))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Note: since Value travels through the stack to get Cell**, we need to use
|
||||
* the annotated location in the tracer instead of the indirect location for
|
||||
* these edges.
|
||||
*/
|
||||
Cell *loc = (Cell *)(trc->realLocation != NULL ? trc->realLocation : thingp);
|
||||
|
||||
AssertStoreBufferContainsEdge(&comp->gcStoreBuffer, loc, dst);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
EndVerifyPostBarriers(JSRuntime *rt)
|
||||
{
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
AutoTraceSession session(rt);
|
||||
|
||||
rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
|
||||
|
||||
AutoCopyFreeListToArenas copy(rt);
|
||||
RecordNativeStackTopForGC(rt);
|
||||
|
||||
VerifyPostTracer *trc = (VerifyPostTracer *)rt->gcVerifyPostData;
|
||||
JS_TracerInit(trc, rt, PostVerifierVisitEdge);
|
||||
trc->count = 0;
|
||||
|
||||
if (!rt->gcExactScanningEnabled)
|
||||
goto oom;
|
||||
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
if (!c->gcStoreBuffer.coalesceForVerification())
|
||||
goto oom;
|
||||
}
|
||||
|
||||
/* Walk the heap. */
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
if (!c->gcStoreBuffer.isEnabled() ||
|
||||
c->gcStoreBuffer.hasOverflowed() ||
|
||||
IsAtomsCompartment(c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c->watchpointMap)
|
||||
c->watchpointMap->markAll(trc);
|
||||
|
||||
for (size_t kind = 0; kind < FINALIZE_LIMIT; ++kind) {
|
||||
for (CellIterUnderGC cells(c, AllocKind(kind)); !cells.done(); cells.next()) {
|
||||
Cell *src = cells.getCell();
|
||||
if (!c->gcNursery.isInside(src))
|
||||
JS_TraceChildren(trc, src, MapAllocToTraceKind(AllocKind(kind)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oom:
|
||||
trc->~VerifyPostTracer();
|
||||
js_free(trc);
|
||||
rt->gcVerifyPostData = NULL;
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
c->gcNursery.disable();
|
||||
c->gcStoreBuffer.disable();
|
||||
c->gcStoreBuffer.releaseVerificationData();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*** Barrier Verifier Scheduling ***/
|
||||
|
||||
static void
|
||||
VerifyPreBarriers(JSRuntime *rt)
|
||||
{
|
||||
if (rt->gcVerifyPreData)
|
||||
EndVerifyPreBarriers(rt);
|
||||
else
|
||||
StartVerifyPreBarriers(rt);
|
||||
}
|
||||
|
||||
static void
|
||||
VerifyPostBarriers(JSRuntime *rt)
|
||||
{
|
||||
if (rt->gcVerifyPostData)
|
||||
EndVerifyPostBarriers(rt);
|
||||
else
|
||||
StartVerifyPostBarriers(rt);
|
||||
}
|
||||
|
||||
void
|
||||
VerifyBarriers(JSRuntime *rt, VerifierType type)
|
||||
{
|
||||
if (type == PreBarrierVerifier)
|
||||
VerifyPreBarriers(rt);
|
||||
else
|
||||
VerifyPostBarriers(rt);
|
||||
}
|
||||
|
||||
static void
|
||||
MaybeVerifyPreBarriers(JSRuntime *rt, bool always)
|
||||
{
|
||||
if (rt->gcZeal() != ZealVerifierPreValue)
|
||||
return;
|
||||
|
||||
if (VerifyPreTracer *trc = (VerifyPreTracer *)rt->gcVerifyPreData) {
|
||||
if (++trc->count < rt->gcZealFrequency && !always)
|
||||
return;
|
||||
|
||||
EndVerifyPreBarriers(rt);
|
||||
}
|
||||
StartVerifyPreBarriers(rt);
|
||||
}
|
||||
|
||||
static void
|
||||
MaybeVerifyPostBarriers(JSRuntime *rt, bool always)
|
||||
{
|
||||
if (rt->gcZeal() != ZealVerifierPostValue)
|
||||
return;
|
||||
|
||||
if (VerifyPostTracer *trc = (VerifyPostTracer *)rt->gcVerifyPostData) {
|
||||
if (++trc->count < rt->gcZealFrequency && !always)
|
||||
return;
|
||||
|
||||
EndVerifyPostBarriers(rt);
|
||||
}
|
||||
StartVerifyPostBarriers(rt);
|
||||
}
|
||||
|
||||
void
|
||||
MaybeVerifyBarriers(JSContext *cx, bool always)
|
||||
{
|
||||
MaybeVerifyPreBarriers(cx->runtime, always);
|
||||
MaybeVerifyPostBarriers(cx->runtime, always);
|
||||
}
|
||||
|
||||
void
|
||||
FinishVerifier(JSRuntime *rt)
|
||||
{
|
||||
if (VerifyPreTracer *trc = (VerifyPreTracer *)rt->gcVerifyPreData) {
|
||||
trc->~VerifyPreTracer();
|
||||
if (VerifyTracer *trc = (VerifyTracer *)rt->gcVerifyData) {
|
||||
trc->~VerifyTracer();
|
||||
js_free(trc);
|
||||
}
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
if (VerifyPostTracer *trc = (VerifyPostTracer *)rt->gcVerifyPostData) {
|
||||
trc->~VerifyPostTracer();
|
||||
js_free(trc);
|
||||
for (CompartmentsIter c(rt); !c.done(); c.next()) {
|
||||
c->gcNursery.disable();
|
||||
c->gcStoreBuffer.disable();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VerifyBarriers(JSRuntime *rt)
|
||||
{
|
||||
if (rt->gcVerifyData)
|
||||
EndVerifyBarriers(rt);
|
||||
else
|
||||
StartVerifyBarriers(rt);
|
||||
}
|
||||
|
||||
void
|
||||
MaybeVerifyBarriers(JSContext *cx, bool always)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
|
||||
if (rt->gcZeal() != ZealVerifierValue)
|
||||
return;
|
||||
|
||||
uint32_t freq = rt->gcZealFrequency;
|
||||
|
||||
if (VerifyTracer *trc = (VerifyTracer *)rt->gcVerifyData) {
|
||||
if (++trc->count < freq && !always)
|
||||
return;
|
||||
|
||||
EndVerifyBarriers(rt);
|
||||
}
|
||||
#endif
|
||||
StartVerifyBarriers(rt);
|
||||
}
|
||||
|
||||
#endif /* JS_GC_ZEAL */
|
||||
|
|
|
@ -112,37 +112,6 @@ MapAllocToTraceKind(AllocKind thingKind)
|
|||
return map[thingKind];
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsNurseryAllocable(AllocKind kind)
|
||||
{
|
||||
JS_ASSERT(kind >= 0 && kind < FINALIZE_LIMIT);
|
||||
static const bool map[FINALIZE_LIMIT] = {
|
||||
false, /* FINALIZE_OBJECT0 */
|
||||
true, /* FINALIZE_OBJECT0_BACKGROUND */
|
||||
false, /* FINALIZE_OBJECT2 */
|
||||
true, /* FINALIZE_OBJECT2_BACKGROUND */
|
||||
false, /* FINALIZE_OBJECT4 */
|
||||
true, /* FINALIZE_OBJECT4_BACKGROUND */
|
||||
false, /* FINALIZE_OBJECT8 */
|
||||
true, /* FINALIZE_OBJECT8_BACKGROUND */
|
||||
false, /* FINALIZE_OBJECT12 */
|
||||
true, /* FINALIZE_OBJECT12_BACKGROUND */
|
||||
false, /* FINALIZE_OBJECT16 */
|
||||
true, /* FINALIZE_OBJECT16_BACKGROUND */
|
||||
false, /* FINALIZE_SCRIPT */
|
||||
false, /* FINALIZE_SHAPE */
|
||||
false, /* FINALIZE_BASE_SHAPE */
|
||||
false, /* FINALIZE_TYPE_OBJECT */
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
false, /* FINALIZE_XML */
|
||||
#endif
|
||||
true, /* FINALIZE_SHORT_STRING */
|
||||
true, /* FINALIZE_STRING */
|
||||
false /* FINALIZE_EXTERNAL_STRING */
|
||||
};
|
||||
return map[kind];
|
||||
}
|
||||
|
||||
inline JSGCTraceKind
|
||||
GetGCThingTraceKind(const void *thing);
|
||||
|
||||
|
@ -1105,26 +1074,19 @@ SetDeterministicGC(JSContext *cx, bool enabled);
|
|||
const int ZealPokeValue = 1;
|
||||
const int ZealAllocValue = 2;
|
||||
const int ZealFrameGCValue = 3;
|
||||
const int ZealVerifierPreValue = 4;
|
||||
const int ZealFrameVerifierPreValue = 5;
|
||||
const int ZealVerifierValue = 4;
|
||||
const int ZealFrameVerifierValue = 5;
|
||||
const int ZealStackRootingSafeValue = 6;
|
||||
const int ZealStackRootingValue = 7;
|
||||
const int ZealIncrementalRootsThenFinish = 8;
|
||||
const int ZealIncrementalMarkAllThenFinish = 9;
|
||||
const int ZealIncrementalMultipleSlices = 10;
|
||||
const int ZealVerifierPostValue = 11;
|
||||
const int ZealFrameVerifierPostValue = 12;
|
||||
|
||||
enum VerifierType {
|
||||
PreBarrierVerifier,
|
||||
PostBarrierVerifier
|
||||
};
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
|
||||
/* Check that write barriers have been used correctly. See jsgc.cpp. */
|
||||
void
|
||||
VerifyBarriers(JSRuntime *rt, VerifierType type);
|
||||
VerifyBarriers(JSRuntime *rt);
|
||||
|
||||
void
|
||||
MaybeVerifyBarriers(JSContext *cx, bool always = false);
|
||||
|
@ -1132,7 +1094,7 @@ MaybeVerifyBarriers(JSContext *cx, bool always = false);
|
|||
#else
|
||||
|
||||
static inline void
|
||||
VerifyBarriers(JSRuntime *rt, VerifierType type)
|
||||
VerifyBarriers(JSRuntime *rt)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -420,11 +420,6 @@ NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
|
|||
|
||||
JS_ASSERT_IF(t && comp->needsBarrier(),
|
||||
static_cast<T *>(t)->arenaHeader()->allocatedDuringIncremental);
|
||||
|
||||
#if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
|
||||
if (cx->runtime->gcVerifyPostData && IsNurseryAllocable(kind) && !IsAtomsCompartment(comp))
|
||||
comp->gcNursery.insertPointer(t);
|
||||
#endif
|
||||
return static_cast<T *>(t);
|
||||
}
|
||||
|
||||
|
@ -447,12 +442,6 @@ TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
|
|||
void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize);
|
||||
JS_ASSERT_IF(t && cx->compartment->needsBarrier(),
|
||||
static_cast<T *>(t)->arenaHeader()->allocatedDuringIncremental);
|
||||
|
||||
#if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
|
||||
JSCompartment *comp = cx->compartment;
|
||||
if (cx->runtime->gcVerifyPostData && IsNurseryAllocable(kind) && !IsAtomsCompartment(comp))
|
||||
comp->gcNursery.insertPointer(t);
|
||||
#endif
|
||||
return static_cast<T *>(t);
|
||||
}
|
||||
|
||||
|
|
|
@ -5097,7 +5097,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, int nb)
|
|||
sn = js_GetSrcNote(jp->script, pc);
|
||||
|
||||
/* Skip any #n= prefix to find the opening bracket. */
|
||||
for (xval = rval; *xval != '[' && *xval != '{' && *xval; xval++)
|
||||
for (xval = rval; *xval != '[' && *xval != '{'; xval++)
|
||||
continue;
|
||||
inArray = (*xval == '[');
|
||||
if (inArray)
|
||||
|
|
|
@ -3822,9 +3822,7 @@ void
|
|||
mjit::Compiler::interruptCheckHelper()
|
||||
{
|
||||
Jump jump;
|
||||
if (cx->runtime->gcZeal() == js::gc::ZealVerifierPreValue ||
|
||||
cx->runtime->gcZeal() == js::gc::ZealVerifierPostValue)
|
||||
{
|
||||
if (cx->runtime->gcZeal() == js::gc::ZealVerifierValue) {
|
||||
/* For barrier verification, always take the interrupt so we can verify. */
|
||||
jump = masm.jump();
|
||||
} else {
|
||||
|
|
Загрузка…
Ссылка в новой задаче