Bug 1458008 - Shuffle Shape flags around to avoid races with off-thread compilation. r=jonco

This commit is contained in:
Jan de Mooij 2018-05-01 13:44:21 +02:00
Родитель f6e81b4de6
Коммит c3e7abdb2c
7 изменённых файлов: 100 добавлений и 120 удалений

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

@ -1112,7 +1112,7 @@ void
MacroAssembler::copySlotsFromTemplate(Register obj, const NativeObject* templateObj,
uint32_t start, uint32_t end)
{
uint32_t nfixed = Min(templateObj->numFixedSlotsForCompilation(), end);
uint32_t nfixed = Min(templateObj->numFixedSlots(), end);
for (unsigned i = start; i < nfixed; i++) {
// Template objects are not exposed to script and therefore immutable.
// However, regexp template objects are sometimes used directly (when
@ -1421,7 +1421,7 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
initGCSlots(obj, temp, ntemplate, initContents);
if (ntemplate->hasPrivate() && !ntemplate->is<TypedArrayObject>()) {
uint32_t nfixed = ntemplate->numFixedSlotsForCompilation();
uint32_t nfixed = ntemplate->numFixedSlots();
Address privateSlot(obj, NativeObject::getPrivateDataOffset(nfixed));
if (ntemplate->is<RegExpObject>()) {
// RegExpObject stores a GC thing (RegExpShared*) in its
@ -3771,7 +3771,7 @@ MacroAssembler::debugAssertObjHasFixedSlots(Register obj, Register scratch)
Label hasFixedSlots;
loadPtr(Address(obj, ShapedObject::offsetOfShape()), scratch);
branchTest32(Assembler::NonZero,
Address(scratch, Shape::offsetOfSlotInfo()),
Address(scratch, Shape::offsetOfImmutableFlags()),
Imm32(Shape::fixedSlotsMask()),
&hasFixedSlots);
assumeUnreachable("Expected a fixed slot");

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

@ -579,9 +579,10 @@ class Shape {
public:
shadow::BaseShape* base;
jsid _1;
uint32_t slotInfo;
uint32_t immutableFlags;
static const uint32_t FIXED_SLOTS_SHIFT = 27;
static const uint32_t FIXED_SLOTS_SHIFT = 24;
static const uint32_t FIXED_SLOTS_MASK = 0x1f << FIXED_SLOTS_SHIFT;
};
/**
@ -597,7 +598,9 @@ struct Object {
static const size_t MAX_FIXED_SLOTS = 16;
size_t numFixedSlots() const { return shape->slotInfo >> Shape::FIXED_SLOTS_SHIFT; }
size_t numFixedSlots() const {
return (shape->immutableFlags & Shape::FIXED_SLOTS_MASK) >> Shape::FIXED_SLOTS_SHIFT;
}
JS::Value* fixedSlots() const {
return (JS::Value*)(uintptr_t(this) + sizeof(shadow::Object));
}

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

@ -292,24 +292,6 @@ js::NativeObject::lookupPure(jsid id)
return Shape::searchNoHashify(lastProperty(), id);
}
uint32_t
js::NativeObject::numFixedSlotsForCompilation() const
{
// This is an alternative method for getting the number of fixed slots in an
// object. It requires more logic and memory accesses than numFixedSlots()
// but is safe to be called from the compilation thread, even if the active
// thread is mutating the VM.
// The compiler does not have access to nursery things.
MOZ_ASSERT(!IsInsideNursery(this));
if (this->is<ArrayObject>())
return 0;
gc::AllocKind kind = asTenured().getAllocKind();
return gc::GetGCKindSlots(kind, getClass());
}
void
NativeObject::setLastPropertyShrinkFixedSlots(Shape* shape)
{

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

@ -717,7 +717,6 @@ class NativeObject : public ShapedObject
uint32_t nslots = lastProperty()->slotSpan(getClass());
return Min(nslots, numFixedSlots());
}
uint32_t numFixedSlotsForCompilation() const;
uint32_t slotSpan() const {
if (inDictionaryMode())

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

@ -181,7 +181,7 @@ Shape::initDictionaryShape(const StackShape& child, uint32_t nfixed, GCPtrShape*
new (this) AccessorShape(child, nfixed);
else
new (this) Shape(child, nfixed);
this->flags |= IN_DICTIONARY;
this->immutableFlags |= IN_DICTIONARY;
this->listp = nullptr;
if (dictp)

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

@ -943,7 +943,8 @@ NativeObject::putDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id
shape->setSlot(slot);
shape->attrs = uint8_t(attrs);
shape->flags = Shape::IN_DICTIONARY;
shape->immutableFlags &= ~Shape::ACCESSOR_SHAPE;
shape->immutableFlags |= Shape::IN_DICTIONARY;
} else {
// Updating the last property in a non-dictionary-mode object. Find an
// alternate shared child of the last property's previous shape.
@ -1037,7 +1038,7 @@ NativeObject::putAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleI
shape->setSlot(SHAPE_INVALID_SLOT);
shape->attrs = uint8_t(attrs);
shape->flags = Shape::IN_DICTIONARY | Shape::ACCESSOR_SHAPE;
shape->immutableFlags |= Shape::IN_DICTIONARY | Shape::ACCESSOR_SHAPE;
AccessorShape& accShape = shape->asAccessorShape();
accShape.rawGetter = getter;
@ -1848,7 +1849,7 @@ Shape::fixupShapeTreeAfterMovingGC()
StackShape lookup(unowned,
const_cast<Shape*>(key)->propidRef(),
key->slotInfo & Shape::SLOT_MASK,
key->immutableFlags & Shape::SLOT_MASK,
key->attrs);
lookup.updateGetterSetter(getter, setter);
e.rekeyFront(lookup, key);
@ -1967,11 +1968,11 @@ Shape::dump(js::GenericPrinter& out) const
out.putChar(')');
}
out.printf("flags %x ", flags);
if (flags) {
out.printf("immutableFlags %x ", immutableFlags);
if (immutableFlags) {
int first = 1;
out.putChar('(');
#define DUMP_FLAG(name, display) if (flags & name) out.put(&(" " #display)[first]), first = 0
#define DUMP_FLAG(name, display) if (immutableFlags & name) out.put(&(" " #display)[first]), first = 0
DUMP_FLAG(IN_DICTIONARY, in_dictionary);
#undef DUMP_FLAG
out.putChar(')');

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

@ -707,36 +707,52 @@ class Shape : public gc::TenuredCell
GCPtrBaseShape base_;
PreBarrieredId propid_;
enum SlotInfo : uint32_t
// Flags that are not modified after the Shape is created. Off-thread Ion
// compilation can access the immutableFlags word, so we don't want any
// mutable state here to avoid (TSan) races.
enum ImmutableFlags : uint32_t
{
/* Number of fixed slots in objects with this shape. */
// FIXED_SLOTS_MAX is the biggest count of fixed slots a Shape can store
// Mask to get the index in object slots for isDataProperty() shapes.
// For other shapes in the property tree with a parent, stores the
// parent's slot index (which may be invalid), and invalid for all
// other shapes.
SLOT_MASK = JS_BIT(24) - 1,
// Number of fixed slots in objects with this shape.
// FIXED_SLOTS_MAX is the biggest count of fixed slots a Shape can store.
FIXED_SLOTS_MAX = 0x1f,
FIXED_SLOTS_SHIFT = 27,
FIXED_SLOTS_SHIFT = 24,
FIXED_SLOTS_MASK = uint32_t(FIXED_SLOTS_MAX << FIXED_SLOTS_SHIFT),
/*
* numLinearSearches starts at zero and is incremented initially on
* search() calls. Once numLinearSearches reaches LINEAR_SEARCHES_MAX,
* the table is created on the next search() call. The table can also
* be created when hashifying for dictionary mode.
*/
LINEAR_SEARCHES_MAX = 0x7,
LINEAR_SEARCHES_SHIFT = 24,
LINEAR_SEARCHES_MASK = LINEAR_SEARCHES_MAX << LINEAR_SEARCHES_SHIFT,
// Property stored in per-object dictionary, not shared property tree.
IN_DICTIONARY = 1 << 29,
/*
* Mask to get the index in object slots for isDataProperty() shapes.
* For other shapes in the property tree with a parent, stores the
* parent's slot index (which may be invalid), and invalid for all
* other shapes.
*/
SLOT_MASK = JS_BIT(24) - 1
// This shape is an AccessorShape, a fat Shape that can store
// getter/setter information.
ACCESSOR_SHAPE = 1 << 30,
};
uint32_t slotInfo; /* mask of above info */
// Flags stored in mutableFlags.
enum MutableFlags : uint8_t {
// numLinearSearches starts at zero and is incremented initially on
// search() calls. Once numLinearSearches reaches LINEAR_SEARCHES_MAX,
// the table is created on the next search() call. The table can also
// be created when hashifying for dictionary mode.
LINEAR_SEARCHES_MAX = 0x7,
LINEAR_SEARCHES_MASK = LINEAR_SEARCHES_MAX,
// Slotful property was stored to more than once. This is used as a
// hint for type inference.
OVERWRITTEN = 0x08,
// Flags used to speed up isBigEnoughForAShapeTable().
HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x10,
CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x20,
};
uint32_t immutableFlags; /* immutable flags, see above */
uint8_t attrs; /* attributes, see jsapi.h JSPROP_* */
uint8_t flags; /* flags, see below for defines */
uint8_t mutableFlags; /* mutable flags, see below for defines */
GCPtrShape parent; /* parent node, reverse for..in order */
/* kids is valid when !inDictionary(), listp is valid when inDictionary(). */
@ -837,8 +853,9 @@ class Shape : public gc::TenuredCell
}
bool isAccessorShape() const {
MOZ_ASSERT_IF(flags & ACCESSOR_SHAPE, getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
return flags & ACCESSOR_SHAPE;
MOZ_ASSERT_IF(immutableFlags & ACCESSOR_SHAPE,
getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
return immutableFlags & ACCESSOR_SHAPE;
}
AccessorShape& asAccessorShape() const {
MOZ_ASSERT(isAccessorShape());
@ -893,32 +910,6 @@ class Shape : public gc::TenuredCell
}
protected:
/*
* Implementation-private bits stored in shape->flags. See public: enum {}
* flags further below, which were allocated FCFS over time, so interleave
* with these bits.
*/
enum {
/* Property stored in per-object dictionary, not shared property tree. */
IN_DICTIONARY = 0x01,
/*
* Slotful property was stored to more than once. This is used as a
* hint for type inference.
*/
OVERWRITTEN = 0x02,
/*
* This shape is an AccessorShape, a fat Shape that can store
* getter/setter information.
*/
ACCESSOR_SHAPE = 0x04,
/* Flags used to speed up isBigEnoughForAShapeTable(). */
HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x08,
CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x10,
};
/* Get a shape identical to this one, without parent/kids information. */
inline Shape(const StackShape& other, uint32_t nfixed);
@ -942,7 +933,7 @@ class Shape : public gc::TenuredCell
public:
bool inDictionary() const {
return (flags & IN_DICTIONARY) != 0;
return immutableFlags & IN_DICTIONARY;
}
inline GetterOp getter() const;
@ -982,10 +973,10 @@ class Shape : public gc::TenuredCell
}
void setOverwritten() {
flags |= OVERWRITTEN;
mutableFlags |= OVERWRITTEN;
}
bool hadOverwrite() const {
return flags & OVERWRITTEN;
return mutableFlags & OVERWRITTEN;
}
bool matches(const Shape* other) const {
@ -1018,7 +1009,7 @@ class Shape : public gc::TenuredCell
}
uint32_t slot() const { MOZ_ASSERT(isDataProperty() && !hasMissingSlot()); return maybeSlot(); }
uint32_t maybeSlot() const {
return slotInfo & SLOT_MASK;
return immutableFlags & SLOT_MASK;
}
bool isEmptyShape() const {
@ -1041,29 +1032,27 @@ class Shape : public gc::TenuredCell
void setSlot(uint32_t slot) {
MOZ_ASSERT(slot <= SHAPE_INVALID_SLOT);
slotInfo = slotInfo & ~Shape::SLOT_MASK;
slotInfo = slotInfo | slot;
immutableFlags = (immutableFlags & ~Shape::SLOT_MASK) | slot;
}
uint32_t numFixedSlots() const {
return slotInfo >> FIXED_SLOTS_SHIFT;
return (immutableFlags & FIXED_SLOTS_MASK) >> FIXED_SLOTS_SHIFT;
}
void setNumFixedSlots(uint32_t nfixed) {
MOZ_ASSERT(nfixed < FIXED_SLOTS_MAX);
slotInfo = slotInfo & ~FIXED_SLOTS_MASK;
slotInfo = slotInfo | (nfixed << FIXED_SLOTS_SHIFT);
immutableFlags = immutableFlags & ~FIXED_SLOTS_MASK;
immutableFlags = immutableFlags | (nfixed << FIXED_SLOTS_SHIFT);
}
uint32_t numLinearSearches() const {
return (slotInfo & LINEAR_SEARCHES_MASK) >> LINEAR_SEARCHES_SHIFT;
return mutableFlags & LINEAR_SEARCHES_MASK;
}
void incrementNumLinearSearches() {
uint32_t count = numLinearSearches();
MOZ_ASSERT(count < LINEAR_SEARCHES_MAX);
slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK;
slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT);
mutableFlags = (mutableFlags & ~LINEAR_SEARCHES_MASK) | (count + 1);
}
const PreBarrieredId& propid() const {
@ -1116,7 +1105,7 @@ class Shape : public gc::TenuredCell
return false;
}
void clearCachedBigEnoughForShapeTable() {
flags &= ~(HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE | CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE);
mutableFlags &= ~(HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE | CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE);
}
public:
@ -1126,18 +1115,18 @@ class Shape : public gc::TenuredCell
// isBigEnoughForAShapeTableSlow is pretty inefficient so we only call
// it once and cache the result.
if (flags & HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE) {
bool res = flags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
if (mutableFlags & HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE) {
bool res = mutableFlags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
MOZ_ASSERT(res == isBigEnoughForAShapeTableSlow());
return res;
}
MOZ_ASSERT(!(flags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE));
MOZ_ASSERT(!(mutableFlags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE));
bool res = isBigEnoughForAShapeTableSlow();
if (res)
flags |= CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
flags |= HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
mutableFlags |= CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
mutableFlags |= HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
return res;
}
@ -1164,7 +1153,7 @@ class Shape : public gc::TenuredCell
#ifdef DEBUG
// For JIT usage.
static inline size_t offsetOfSlotInfo() { return offsetof(Shape, slotInfo); }
static inline size_t offsetOfImmutableFlags() { return offsetof(Shape, immutableFlags); }
static inline uint32_t fixedSlotsMask() { return FIXED_SLOTS_MASK; }
#endif
@ -1178,8 +1167,9 @@ class Shape : public gc::TenuredCell
static void staticAsserts() {
JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));
JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo));
JS_STATIC_ASSERT(offsetof(Shape, immutableFlags) == offsetof(js::shadow::Shape, immutableFlags));
JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT);
JS_STATIC_ASSERT(FIXED_SLOTS_MASK == js::shadow::Shape::FIXED_SLOTS_MASK);
}
};
@ -1440,9 +1430,9 @@ struct StackShape
jsid propid;
GetterOp rawGetter;
SetterOp rawSetter;
uint32_t slot_;
uint32_t immutableFlags;
uint8_t attrs;
uint8_t flags;
uint8_t mutableFlags;
explicit StackShape(UnownedBaseShape* base, jsid propid, uint32_t slot,
unsigned attrs)
@ -1450,9 +1440,9 @@ struct StackShape
propid(propid),
rawGetter(nullptr),
rawSetter(nullptr),
slot_(slot),
immutableFlags(slot),
attrs(uint8_t(attrs)),
flags(0)
mutableFlags(0)
{
MOZ_ASSERT(base);
MOZ_ASSERT(!JSID_IS_VOID(propid));
@ -1464,16 +1454,16 @@ struct StackShape
propid(shape->propidRef()),
rawGetter(shape->getter()),
rawSetter(shape->setter()),
slot_(shape->maybeSlot()),
immutableFlags(shape->immutableFlags),
attrs(shape->attrs),
flags(shape->flags)
mutableFlags(shape->mutableFlags)
{}
void updateGetterSetter(GetterOp rawGetter, SetterOp rawSetter) {
if (rawGetter || rawSetter || (attrs & (JSPROP_GETTER|JSPROP_SETTER)))
flags |= Shape::ACCESSOR_SHAPE;
immutableFlags |= Shape::ACCESSOR_SHAPE;
else
flags &= ~Shape::ACCESSOR_SHAPE;
immutableFlags &= ~Shape::ACCESSOR_SHAPE;
this->rawGetter = rawGetter;
this->rawSetter = rawSetter;
@ -1485,22 +1475,25 @@ struct StackShape
}
bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
uint32_t slot() const { MOZ_ASSERT(isDataProperty() && !hasMissingSlot()); return slot_; }
uint32_t maybeSlot() const { return slot_; }
uint32_t slot() const {
MOZ_ASSERT(isDataProperty() && !hasMissingSlot());
return maybeSlot();
}
uint32_t maybeSlot() const { return immutableFlags & Shape::SLOT_MASK; }
void setSlot(uint32_t slot) {
MOZ_ASSERT(slot <= SHAPE_INVALID_SLOT);
slot_ = slot;
immutableFlags = (immutableFlags & ~Shape::SLOT_MASK) | slot;
}
bool isAccessorShape() const {
return flags & Shape::ACCESSOR_SHAPE;
return immutableFlags & Shape::ACCESSOR_SHAPE;
}
HashNumber hash() const {
HashNumber hash = HashId(propid);
return mozilla::AddToHash(hash,
mozilla::HashGeneric(base, attrs, slot_, rawGetter, rawSetter));
mozilla::HashGeneric(base, attrs, maybeSlot(), rawGetter, rawSetter));
}
// Traceable implementation.
@ -1542,11 +1535,13 @@ inline
Shape::Shape(const StackShape& other, uint32_t nfixed)
: base_(other.base),
propid_(other.propid),
slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
immutableFlags(other.immutableFlags),
attrs(other.attrs),
flags(other.flags),
mutableFlags(other.mutableFlags),
parent(nullptr)
{
setNumFixedSlots(nfixed);
#ifdef DEBUG
gc::AllocKind allocKind = getAllocKind();
MOZ_ASSERT_IF(other.isAccessorShape(), allocKind == gc::AllocKind::ACCESSOR_SHAPE);
@ -1574,9 +1569,9 @@ inline
Shape::Shape(UnownedBaseShape* base, uint32_t nfixed)
: base_(base),
propid_(JSID_EMPTY),
slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
immutableFlags(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
attrs(0),
flags(0),
mutableFlags(0),
parent(nullptr)
{
MOZ_ASSERT(base);
@ -1625,7 +1620,7 @@ inline bool
Shape::matches(const StackShape& other) const
{
return propid_.get() == other.propid &&
matchesParamsAfterId(other.base, other.slot_, other.attrs,
matchesParamsAfterId(other.base, other.maybeSlot(), other.attrs,
other.rawGetter, other.rawSetter);
}