Bug 1625212 - Add CellHeaderWithTenuredGCPointer and use it for Shape r=sfink

Differential Revision: https://phabricator.services.mozilla.com/D68831

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jon Coppeard 2020-04-01 09:43:06 +00:00
Родитель 42cc5e671d
Коммит ecbd72e069
6 изменённых файлов: 104 добавлений и 15 удалений

Просмотреть файл

@ -679,6 +679,62 @@ class CellHeaderWithNonGCPointer : public CellHeader {
}
};
// Base class for GC things that have a tenured GC pointer as their first word.
//
// The low bits of the first word (see CellFlagBitsReservedForGC) are reserved
// for GC.
//
// This includes a pre write barrier when the pointer is update. No post barrier
// is necessary as the pointer is always tenured.
template <class PtrT>
class CellHeaderWithTenuredGCPointer : public CellHeader {
static_assert(!std::is_pointer<PtrT>::value,
"PtrT should be the type of the referent, not of the pointer");
static_assert(
std::is_base_of<Cell, PtrT>::value,
"Only use CellHeaderWithTenuredGCPointer for pointers to GC things");
public:
CellHeaderWithTenuredGCPointer() = default;
explicit CellHeaderWithTenuredGCPointer(PtrT* initial) : CellHeader() {
initPtr(initial);
}
void initPtr(PtrT* initial) {
MOZ_ASSERT(!IsInsideNursery(initial));
uintptr_t data = uintptr_t(initial);
MOZ_ASSERT((data & RESERVED_MASK) == 0);
this->header_ = data;
}
PtrT* ptr() const {
// Currently we never observe any flags set here because this base class is
// only used for GC things that are always tenured (for which the nursery
// kind flags are also always clear). This means we don't need to use
// masking to get and set the pointer.
MOZ_ASSERT(flags() == 0);
return reinterpret_cast<PtrT*>(this->header_);
}
void setPtr(PtrT* newValue) {
// As above, no flags are expected to be set here.
MOZ_ASSERT(!IsInsideNursery(newValue));
PtrT::writeBarrierPre(ptr());
unsafeSetPtr(newValue);
}
void unsafeSetPtr(PtrT* newValue) {
uintptr_t data = uintptr_t(newValue);
MOZ_ASSERT(flags() == 0);
MOZ_ASSERT((data & RESERVED_MASK) == 0);
this->header_ = data;
}
static constexpr size_t offsetOfPtr() {
return offsetof(CellHeaderWithTenuredGCPointer, header_);
}
};
} /* namespace gc */
} /* namespace js */

Просмотреть файл

@ -1126,7 +1126,7 @@ void BaseScript::traceChildren(JSTracer* trc) {
}
void Shape::traceChildren(JSTracer* trc) {
TraceEdge(trc, &base_, "base");
TraceEdge(trc, &headerAndBase_, "base");
TraceEdge(trc, &propidRef(), "propid");
if (parent) {
TraceEdge(trc, &parent, "parent");

Просмотреть файл

@ -131,6 +131,17 @@ inline void TraceEdge(JSTracer* trc, WeakHeapPtr<T>* thingp, const char* name) {
gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp->unsafeGet()), name);
}
template <class T>
inline void TraceEdge(JSTracer* trc,
gc::CellHeaderWithTenuredGCPointer<T>* thingp,
const char* name) {
T* thing = thingp->ptr();
gc::TraceEdgeInternal(trc, gc::ConvertToBase(&thing), name);
if (thing != thingp->ptr()) {
thingp->unsafeSetPtr(thing);
}
}
// Trace through a possibly-null edge in the live object graph on behalf of
// tracing.
@ -150,6 +161,19 @@ inline void TraceNullableEdge(JSTracer* trc, WeakHeapPtr<T>* thingp,
}
}
template <class T>
inline void TraceNullableEdge(JSTracer* trc,
gc::CellHeaderWithTenuredGCPointer<T>* thingp,
const char* name) {
T* thing = thingp->ptr();
if (thing) {
gc::TraceEdgeInternal(trc, gc::ConvertToBase(&thing), name);
if (thing != thingp->ptr()) {
thingp->unsafeSetPtr(thing);
}
}
}
// Trace through a "root" edge. These edges are the initial edges in the object
// graph traversal. Root edges are asserted to only be traversed in the initial
// phase of a GC.

Просмотреть файл

@ -125,9 +125,9 @@ inline Shape* Shape::new_(JSContext* cx, Handle<StackShape> other,
}
inline void Shape::updateBaseShapeAfterMovingGC() {
BaseShape* base = base_;
BaseShape* base = this->base();
if (IsForwarded(base)) {
base_.unsafeSet(Forwarded(base));
headerAndBase_.unsafeSetPtr(Forwarded(base));
}
}

Просмотреть файл

@ -136,7 +136,7 @@ bool Shape::makeOwnBaseShape(JSContext* cx) {
new (nbase) BaseShape(StackBaseShape(this));
nbase->setOwned(base()->toUnowned());
this->base_ = nbase;
setBase(nbase);
return true;
}
@ -155,10 +155,10 @@ void Shape::handoffTableTo(Shape* shape) {
MOZ_ASSERT_IF(!shape->isEmptyShape() && shape->isDataProperty(),
nbase->slotSpan() > shape->slot());
this->base_ = nbase->baseUnowned();
setBase(nbase->baseUnowned());
nbase->adoptUnowned(shape->base()->toUnowned());
shape->base_ = nbase;
shape->setBase(nbase);
}
/* static */
@ -1090,7 +1090,7 @@ Shape* NativeObject::putDataProperty(JSContext* cx, HandleNativeObject obj,
if (updateLast) {
shape->base()->adoptUnowned(nbase);
} else {
shape->base_ = nbase;
shape->setBase(nbase);
}
shape->setSlot(slot);
@ -1196,7 +1196,7 @@ Shape* NativeObject::putAccessorProperty(JSContext* cx, HandleNativeObject obj,
if (updateLast) {
shape->base()->adoptUnowned(nbase);
} else {
shape->base_ = nbase;
shape->setBase(nbase);
}
shape->setSlot(SHAPE_INVALID_SLOT);
@ -1324,7 +1324,7 @@ bool NativeObject::removeProperty(JSContext* cx, HandleNativeObject obj,
if (!nbase) {
return false;
}
previous->base_ = nbase;
previous->setBase(nbase);
}
}

Просмотреть файл

@ -947,7 +947,8 @@ class Shape : public gc::TenuredCell {
friend class js::gc::RelocationOverlay;
protected:
GCPtrBaseShape base_;
using HeaderWithBaseShape = gc::CellHeaderWithTenuredGCPointer<BaseShape>;
HeaderWithBaseShape headerAndBase_;
const GCPtrId propid_;
// Flags that are not modified after the Shape is created. Off-thread Ion
@ -1272,7 +1273,7 @@ class Shape : public gc::TenuredCell {
attrs == aattrs && getter() == rawGetter && setter() == rawSetter;
}
BaseShape* base() const { return base_.get(); }
BaseShape* base() const { return headerAndBase_.ptr(); }
static bool isDataProperty(unsigned attrs, GetterOp getter, SetterOp setter) {
return !(attrs & (JSPROP_GETTER | JSPROP_SETTER)) && !getter && !setter;
@ -1370,6 +1371,11 @@ class Shape : public gc::TenuredCell {
}
private:
void setBase(BaseShape* base) {
MOZ_ASSERT(base);
headerAndBase_.setPtr(base);
}
bool isBigEnoughForAShapeTableSlow() {
uint32_t count = 0;
for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront()) {
@ -1419,6 +1425,7 @@ class Shape : public gc::TenuredCell {
void removeChild(JSFreeOp* fop, Shape* child);
static const JS::TraceKind TraceKind = JS::TraceKind::Shape;
const gc::CellHeader& cellHeader() const { return headerAndBase_; }
void traceChildren(JSTracer* trc);
@ -1430,7 +1437,9 @@ class Shape : public gc::TenuredCell {
void updateBaseShapeAfterMovingGC();
// For JIT usage.
static inline size_t offsetOfBaseShape() { return offsetof(Shape, base_); }
static constexpr size_t offsetOfBaseShape() {
return offsetof(Shape, headerAndBase_) + HeaderWithBaseShape::offsetOfPtr();
}
#ifdef DEBUG
static inline size_t offsetOfImmutableFlags() {
@ -1444,7 +1453,7 @@ class Shape : public gc::TenuredCell {
void fixupShapeTreeAfterMovingGC();
static void staticAsserts() {
static_assert(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));
static_assert(offsetOfBaseShape() == offsetof(js::shadow::Shape, base));
static_assert(offsetof(Shape, immutableFlags) ==
offsetof(js::shadow::Shape, immutableFlags));
static_assert(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT);
@ -1712,7 +1721,7 @@ class MutableWrappedPtrOperations<StackShape, Wrapper>
};
inline Shape::Shape(const StackShape& other, uint32_t nfixed)
: base_(other.base),
: headerAndBase_(other.base),
propid_(other.propid),
immutableFlags(other.immutableFlags),
attrs(other.attrs),
@ -1744,7 +1753,7 @@ class NurseryShapesRef : public gc::BufferableRef {
};
inline Shape::Shape(UnownedBaseShape* base, uint32_t nfixed)
: base_(base),
: headerAndBase_(base),
propid_(JSID_EMPTY),
immutableFlags(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
attrs(0),