Bug 1723085 part 2 - Optimize getting/setting reserved slots inside the JS engine. r=jonco

After removing private slots, we can optimize gets/sets of reserved slots by
relying on the fact that fixed slots are used for them (up to the maximum number
of fixed slots).

This means we no longer need to read obj->shape->numFixedSlots when getting/setting
reserved slots and can optimize a slot access with constant index (the common case)
to a single memory load/store.

Unfortunately doing the same for the JS::GetReservedSlot and JS::SetReservedSlot
APIs is more complicated because these can also be used for proxies and swappable
DOM objects.

Differential Revision: https://phabricator.services.mozilla.com/D121356
This commit is contained in:
Jan de Mooij 2021-07-31 19:48:56 +00:00
parent d6bd2be0c6
commit 16e64b6d3f
3 changed files with 43 additions and 13 deletions

View File

@ -2075,13 +2075,19 @@ JS_PUBLIC_API void JS_SetAllNonReservedSlotsToUndefined(JS::HandleObject obj) {
JS_PUBLIC_API void JS_SetReservedSlot(JSObject* obj, uint32_t index,
const Value& value) {
obj->as<NativeObject>().setReservedSlot(index, value);
// Note: we don't use setReservedSlot so that this also works on swappable DOM
// objects. See NativeObject::getReservedSlotRef comment.
MOZ_ASSERT(index < JSCLASS_RESERVED_SLOTS(obj->getClass()));
obj->as<NativeObject>().setSlot(index, value);
}
JS_PUBLIC_API void JS_InitReservedSlot(JSObject* obj, uint32_t index, void* ptr,
size_t nbytes, JS::MemoryUse use) {
InitReservedSlot(&obj->as<NativeObject>(), index, ptr, nbytes,
js::MemoryUse(use));
// Note: we don't use InitReservedSlot so that this also works on swappable
// DOM objects. See NativeObject::getReservedSlotRef comment.
MOZ_ASSERT(index < JSCLASS_RESERVED_SLOTS(obj->getClass()));
AddCellMemory(obj, nbytes, js::MemoryUse(use));
obj->as<NativeObject>().initSlot(index, PrivateValue(ptr));
}
JS_PUBLIC_API bool JS::IsMapObject(JSContext* cx, JS::HandleObject obj,

View File

@ -446,6 +446,8 @@ void JS::detail::SetReservedSlotWithBarrier(JSObject* obj, size_t slot,
if (obj->is<ProxyObject>()) {
obj->as<ProxyObject>().setReservedSlot(slot, value);
} else {
// Note: we don't use setReservedSlot so that this also works on swappable
// DOM objects. See NativeObject::getReservedSlotRef comment.
obj->as<NativeObject>().setSlot(slot, value);
}
}

View File

@ -1123,20 +1123,42 @@ class NativeObject : public JSObject {
inline void shiftDenseElementsUnchecked(uint32_t count);
// Like getSlotRef, but optimized for reserved slots. This relies on the fact
// that the first reserved slots (up to MAX_FIXED_SLOTS) are always stored in
// fixed slots. This lets the compiler optimize away the branch below when
// |index| is a constant (after inlining).
//
// Note: objects that may be swapped have less predictable slot layouts
// because they could have been swapped with an object with fewer fixed slots.
// Fortunately, the only native objects that can be swapped are DOM objects
// and these shouldn't end up here (asserted below).
MOZ_ALWAYS_INLINE HeapSlot& getReservedSlotRef(uint32_t index) {
MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
MOZ_ASSERT(slotIsFixed(index) == (index < MAX_FIXED_SLOTS));
MOZ_ASSERT(!ObjectMayBeSwapped(this));
return index < MAX_FIXED_SLOTS ? fixedSlots()[index]
: slots_[index - MAX_FIXED_SLOTS];
}
MOZ_ALWAYS_INLINE const HeapSlot& getReservedSlotRef(uint32_t index) const {
MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
MOZ_ASSERT(slotIsFixed(index) == (index < MAX_FIXED_SLOTS));
MOZ_ASSERT(!ObjectMayBeSwapped(this));
return index < MAX_FIXED_SLOTS ? fixedSlots()[index]
: slots_[index - MAX_FIXED_SLOTS];
}
public:
MOZ_ALWAYS_INLINE const Value& getReservedSlot(uint32_t index) const {
MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
return getSlot(index);
return getReservedSlotRef(index);
}
MOZ_ALWAYS_INLINE void initReservedSlot(uint32_t index, const Value& v) {
MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
initSlot(index, v);
MOZ_ASSERT(getReservedSlot(index).isUndefined());
checkStoredValue(v);
getReservedSlotRef(index).init(this, HeapSlot::Slot, index, v);
}
MOZ_ALWAYS_INLINE void setReservedSlot(uint32_t index, const Value& v) {
MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
setSlot(index, v);
checkStoredValue(v);
getReservedSlotRef(index).set(this, HeapSlot::Slot, index, v);
}
// For slots which are known to always be fixed, due to the way they are
@ -1482,11 +1504,11 @@ class NativeObject : public JSObject {
gc::Cell* cell) {
MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(getClass()));
MOZ_ASSERT(cell);
getSlotAddress(slot)->unbarrieredSet(PrivateValue(cell));
getReservedSlotRef(slot).unbarrieredSet(PrivateValue(cell));
}
void clearReservedSlotGCThingAsPrivate(uint32_t slot) {
MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(getClass()));
HeapSlot* pslot = getSlotAddress(slot);
HeapSlot* pslot = &getReservedSlotRef(slot);
if (!pslot->isUndefined()) {
privatePreWriteBarrier(pslot);
pslot->unbarrieredSet(UndefinedValue());