Bug 1341355 - Refactor the mark stack r=sfink

This commit is contained in:
Jon Coppeard 2017-02-21 17:53:21 +00:00
parent 7b865b5def
commit e6282397f4
2 changed files with 167 additions and 104 deletions

View File

@ -897,18 +897,18 @@ template <> void GCMarker::traverse(js::Scope* thing) { markAndScan(thing); }
// be used as a weakmap key and thereby recurse into weakmapped values.
template <typename T>
void
js::GCMarker::markAndPush(StackTag tag, T* thing)
js::GCMarker::markAndPush(T* thing)
{
if (!mark(thing))
return;
pushTaggedPtr(tag, thing);
pushTaggedPtr(thing);
markImplicitEdges(thing);
}
namespace js {
template <> void GCMarker::traverse(JSObject* thing) { markAndPush(ObjectTag, thing); }
template <> void GCMarker::traverse(ObjectGroup* thing) { markAndPush(GroupTag, thing); }
template <> void GCMarker::traverse(jit::JitCode* thing) { markAndPush(JitCodeTag, thing); }
template <> void GCMarker::traverse(JSScript* thing) { markAndPush(ScriptTag, thing); }
template <> void GCMarker::traverse(JSObject* thing) { markAndPush(thing); }
template <> void GCMarker::traverse(ObjectGroup* thing) { markAndPush(thing); }
template <> void GCMarker::traverse(jit::JitCode* thing) { markAndPush(thing); }
template <> void GCMarker::traverse(JSScript* thing) { markAndPush(thing); }
} // namespace js
namespace js {
@ -1192,7 +1192,7 @@ js::GCMarker::eagerlyMarkChildren(JSRope* rope)
} else {
// When both children are ropes, set aside the right one to
// scan it later.
if (next && !stack.push(reinterpret_cast<uintptr_t>(next)))
if (next && !stack.pushTempRope(next))
delayMarkingChildren(next);
next = &left->asRope();
}
@ -1201,7 +1201,9 @@ js::GCMarker::eagerlyMarkChildren(JSRope* rope)
rope = next;
} else if (savedPos != stack.position()) {
MOZ_ASSERT(savedPos < stack.position());
rope = reinterpret_cast<JSRope*>(stack.pop());
uintptr_t ptr = stack.pop();
MOZ_ASSERT((ptr & MarkStack::TagMask) == MarkStack::TempRopeTag);
rope = reinterpret_cast<JSRope*>(ptr &~ MarkStack::TempRopeTag);
} else {
break;
}
@ -1646,13 +1648,13 @@ GCMarker::processMarkStackTop(SliceBudget& budget)
// Decode
uintptr_t addr = stack.pop();
uintptr_t tag = addr & StackTagMask;
addr &= ~StackTagMask;
uintptr_t tag = addr & MarkStack::TagMask;
addr &= ~MarkStack::TagMask;
// Dispatch
switch (tag) {
case ValueArrayTag: {
JS_STATIC_ASSERT(ValueArrayTag == 0);
case MarkStack::ValueArrayTag: {
JS_STATIC_ASSERT(MarkStack::ValueArrayTag == 0);
MOZ_ASSERT(!(addr & CellMask));
obj = reinterpret_cast<JSObject*>(addr);
uintptr_t addr2 = stack.pop();
@ -1664,25 +1666,25 @@ GCMarker::processMarkStackTop(SliceBudget& budget)
goto scan_value_array;
}
case ObjectTag: {
case MarkStack::ObjectTag: {
obj = reinterpret_cast<JSObject*>(addr);
AssertZoneIsMarking(obj);
goto scan_obj;
}
case GroupTag: {
case MarkStack::GroupTag: {
return lazilyMarkChildren(reinterpret_cast<ObjectGroup*>(addr));
}
case JitCodeTag: {
case MarkStack::JitCodeTag: {
return reinterpret_cast<jit::JitCode*>(addr)->traceChildren(this);
}
case ScriptTag: {
case MarkStack::ScriptTag: {
return reinterpret_cast<JSScript*>(addr)->traceChildren(this);
}
case SavedValueArrayTag: {
case MarkStack::SavedValueArrayTag: {
MOZ_ASSERT(!(addr & CellMask));
JSObject* obj = reinterpret_cast<JSObject*>(addr);
HeapSlot* vp;
@ -1819,10 +1821,10 @@ struct SlotArrayLayout
void
GCMarker::saveValueRanges()
{
for (uintptr_t* p = stack.tos_; p > stack.stack_; ) {
uintptr_t tag = *--p & StackTagMask;
if (tag == ValueArrayTag) {
*p &= ~StackTagMask;
for (uintptr_t* p = stack.top(); p > stack.base(); ) {
uintptr_t tag = *--p & MarkStack::TagMask;
if (tag == MarkStack::ValueArrayTag) {
*p &= ~MarkStack::TagMask;
p -= 2;
SlotArrayLayout* arr = reinterpret_cast<SlotArrayLayout*>(p);
NativeObject* obj = arr->obj;
@ -1848,8 +1850,8 @@ GCMarker::saveValueRanges()
}
arr->kind = HeapSlot::Slot;
}
p[2] |= SavedValueArrayTag;
} else if (tag == SavedValueArrayTag) {
p[2] |= MarkStack::SavedValueArrayTag;
} else if (tag == MarkStack::SavedValueArrayTag) {
p -= 2;
}
}
@ -1904,6 +1906,26 @@ GCMarker::restoreValueArray(JSObject* objArg, void** vpp, void** endp)
/*** Mark Stack ***********************************************************************************/
template <typename T>
struct MapTypeToMarkStackTag {};
template <>
struct MapTypeToMarkStackTag<JSObject*> { static const auto value = MarkStack::ObjectTag; };
template <>
struct MapTypeToMarkStackTag<ObjectGroup*> { static const auto value = MarkStack::GroupTag; };
template <>
struct MapTypeToMarkStackTag<jit::JitCode*> { static const auto value = MarkStack::JitCodeTag; };
template <>
struct MapTypeToMarkStackTag<JSScript*> { static const auto value = MarkStack::ScriptTag; };
MarkStack::MarkStack(size_t maxCapacity)
: stack_(nullptr),
tos_(nullptr),
end_(nullptr),
baseCapacity_(0),
maxCapacity_(maxCapacity)
{}
bool
MarkStack::init(JSGCMode gcMode)
{
@ -1949,6 +1971,51 @@ MarkStack::setMaxCapacity(size_t maxCapacity)
reset();
}
template <typename T>
inline bool
MarkStack::push(T* ptr)
{
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
MOZ_ASSERT(!(addr & TagMask));
auto tag = MapTypeToMarkStackTag<T*>::value;
return pushTaggedPtr(addr | uintptr_t(tag));
}
inline bool
MarkStack::pushTempRope(JSRope* rope)
{
return pushTaggedPtr(uintptr_t(rope) | TempRopeTag);
}
inline bool
MarkStack::pushTaggedPtr(uintptr_t item)
{
if (tos_ == end_) {
if (!enlarge(1))
return false;
}
MOZ_ASSERT(tos_ < end_);
*tos_++ = item;
return true;
}
inline bool
MarkStack::push(uintptr_t item1, uintptr_t item2, uintptr_t item3)
{
uintptr_t* nextTos = tos_ + 3;
if (nextTos > end_) {
if (!enlarge(3))
return false;
nextTos = tos_ + 3;
}
MOZ_ASSERT(nextTos <= end_);
tos_[0] = item1;
tos_[1] = item2;
tos_[2] = item3;
tos_ = nextTos;
return true;
}
void
MarkStack::reset()
{
@ -2089,6 +2156,41 @@ GCMarker::reset()
MOZ_ASSERT(!markLaterArenas);
}
template <typename T>
void
GCMarker::pushTaggedPtr(T* ptr)
{
checkZone(ptr);
if (!stack.push(ptr))
delayMarkingChildren(ptr);
}
void
GCMarker::pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end)
{
checkZone(obj);
MOZ_ASSERT(start <= end);
uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | MarkStack::ValueArrayTag;
uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
/*
* Push in the reverse order so obj will be on top. If we cannot push
* the array, we trigger delay marking for the whole object.
*/
if (!stack.push(endAddr, startAddr, tagged))
delayMarkingChildren(obj);
}
void
GCMarker::repush(JSObject* obj)
{
MOZ_ASSERT(gc::TenuredCell::fromPointer(obj)->isMarked(markColor()));
pushTaggedPtr(obj);
}
void
GCMarker::enterWeakMarkingMode()
{

View File

@ -41,6 +41,8 @@ class JitCode;
static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
namespace gc {
/*
* When the native stack is low, the GC does not call js::TraceChildren to mark
* the reachable "children" of the thing. Rather the thing is put aside and
@ -55,8 +57,6 @@ static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
*/
class MarkStack
{
friend class GCMarker;
ActiveThreadData<uintptr_t*> stack_;
ActiveThreadData<uintptr_t*> tos_;
ActiveThreadData<uintptr_t*> end_;
@ -66,13 +66,28 @@ class MarkStack
ActiveThreadData<size_t> maxCapacity_;
public:
explicit MarkStack(size_t maxCapacity)
: stack_(nullptr),
tos_(nullptr),
end_(nullptr),
baseCapacity_(0),
maxCapacity_(maxCapacity)
{}
/*
* We use a common mark stack to mark GC things of different types and use
* the explicit tags to distinguish them when it cannot be deduced from
* the context of push or pop operation.
*/
enum Tag {
ValueArrayTag,
ObjectTag,
GroupTag,
SavedValueArrayTag,
JitCodeTag,
ScriptTag,
TempRopeTag,
LastTag = TempRopeTag
};
static const uintptr_t TagMask = 7;
static_assert(TagMask >= uintptr_t(LastTag), "The tag mask must subsume the tags.");
static_assert(TagMask <= gc::CellMask, "The tag mask must be embeddable in a Cell*.");
explicit MarkStack(size_t maxCapacity);
~MarkStack() {
js_free(stack_);
@ -94,30 +109,16 @@ class MarkStack
size_t maxCapacity() const { return maxCapacity_; }
void setMaxCapacity(size_t maxCapacity);
MOZ_MUST_USE bool push(uintptr_t item) {
if (tos_ == end_) {
if (!enlarge(1))
return false;
}
MOZ_ASSERT(tos_ < end_);
*tos_++ = item;
return true;
}
uintptr_t* base() { return stack_; }
uintptr_t* top() { return tos_; }
MOZ_MUST_USE bool push(uintptr_t item1, uintptr_t item2, uintptr_t item3) {
uintptr_t* nextTos = tos_ + 3;
if (nextTos > end_) {
if (!enlarge(3))
return false;
nextTos = tos_ + 3;
}
MOZ_ASSERT(nextTos <= end_);
tos_[0] = item1;
tos_[1] = item2;
tos_[2] = item3;
tos_ = nextTos;
return true;
}
template <typename T>
MOZ_MUST_USE bool push(T* ptr);
MOZ_MUST_USE bool push(uintptr_t item1, uintptr_t item2, uintptr_t item3);
// GCMarker::eagerlyMarkChildren uses unused marking stack as temporary
// storage to hold rope pointers.
MOZ_MUST_USE bool pushTempRope(JSRope* ptr);
bool isEmpty() const {
return tos_ == stack_;
@ -136,9 +137,10 @@ class MarkStack
void setGCMode(JSGCMode gcMode);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
};
namespace gc {
private:
MOZ_MUST_USE bool pushTaggedPtr(uintptr_t item);
};
struct WeakKeyTableHashPolicy {
typedef JS::GCCellPtr Lookup;
@ -252,34 +254,12 @@ class GCMarker : public JSTracer
void checkZone(void* p) {}
#endif
/*
* We use a common mark stack to mark GC things of different types and use
* the explicit tags to distinguish them when it cannot be deduced from
* the context of push or pop operation.
*/
enum StackTag {
ValueArrayTag,
ObjectTag,
GroupTag,
SavedValueArrayTag,
JitCodeTag,
ScriptTag,
LastTag = JitCodeTag
};
static const uintptr_t StackTagMask = 7;
static_assert(StackTagMask >= uintptr_t(LastTag), "The tag mask must subsume the tags.");
static_assert(StackTagMask <= gc::CellMask, "The tag mask must be embeddable in a Cell*.");
// Push an object onto the stack for later tracing and assert that it has
// already been marked.
void repush(JSObject* obj) {
MOZ_ASSERT(gc::TenuredCell::fromPointer(obj)->isMarked(markColor()));
pushTaggedPtr(ObjectTag, obj);
}
inline void repush(JSObject* obj);
template <typename T> void markAndTraceChildren(T* thing);
template <typename T> void markAndPush(StackTag tag, T* thing);
template <typename T> void markAndPush(T* thing);
template <typename T> void markAndScan(T* thing);
template <typename T> void markImplicitEdgesHelper(T oldThing);
template <typename T> void markImplicitEdges(T* oldThing);
@ -300,29 +280,10 @@ class GCMarker : public JSTracer
template <typename T>
MOZ_MUST_USE bool mark(T* thing);
void pushTaggedPtr(StackTag tag, void* ptr) {
checkZone(ptr);
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
MOZ_ASSERT(!(addr & StackTagMask));
if (!stack.push(addr | uintptr_t(tag)))
delayMarkingChildren(ptr);
}
template <typename T>
inline void pushTaggedPtr(T* ptr);
void pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end) {
checkZone(obj);
MOZ_ASSERT(start <= end);
uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | GCMarker::ValueArrayTag;
uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
/*
* Push in the reverse order so obj will be on top. If we cannot push
* the array, we trigger delay marking for the whole object.
*/
if (!stack.push(endAddr, startAddr, tagged))
delayMarkingChildren(obj);
}
inline void pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end);
bool isMarkStackEmpty() {
return stack.isEmpty();
@ -333,7 +294,7 @@ class GCMarker : public JSTracer
inline void processMarkStackTop(SliceBudget& budget);
/* The mark stack. Pointers in this stack are "gray" in the GC sense. */
MarkStack stack;
gc::MarkStack stack;
/* The color is only applied to objects and functions. */
ActiveThreadData<uint32_t> color;