Bug 1116855 - Various fixes and VM changes for JIT'ing unboxed objects, r=jandem.

This commit is contained in:
Brian Hackett 2015-01-31 12:16:52 -07:00
parent 958c00e0fe
commit 1a99000d97
10 changed files with 113 additions and 76 deletions

View File

@ -1700,7 +1700,7 @@ TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
return obj_lookupElement(cx, obj, index, objp, propp);
if (JSID_IS_ATOM(id, cx->names().length)) {
MarkNonNativePropertyFound(propp);
MarkNonNativePropertyFound<CanGC>(propp);
objp.set(obj);
return true;
}
@ -1712,7 +1712,7 @@ TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
StructTypeDescr &structDescr = descr->as<StructTypeDescr>();
size_t index;
if (structDescr.fieldIndex(id, &index)) {
MarkNonNativePropertyFound(propp);
MarkNonNativePropertyFound<CanGC>(propp);
objp.set(obj);
return true;
}
@ -1746,7 +1746,7 @@ TypedObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
MutableHandleObject objp, MutableHandleShape propp)
{
MOZ_ASSERT(obj->is<TypedObject>());
MarkNonNativePropertyFound(propp);
MarkNonNativePropertyFound<CanGC>(propp);
objp.set(obj);
return true;
}

View File

@ -1177,9 +1177,17 @@ CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &ca
// of turn. We don't mind doing this even when purity isn't required, because we
// only miss out on shape hashification, which is only a temporary perf cost.
// The limits were arbitrarily set, anyways.
if (!LookupPropertyPure(cx, obj, NameToId(name), holder.address(), shape.address()))
JSObject *baseHolder = nullptr;
if (!LookupPropertyPure(cx, obj, NameToId(name), &baseHolder, shape.address()))
return GetPropertyIC::CanAttachNone;
MOZ_ASSERT(!holder);
if (baseHolder) {
if (!baseHolder->isNative())
return GetPropertyIC::CanAttachNone;
holder.set(&baseHolder->as<NativeObject>());
}
RootedScript script(cx);
jsbytecode *pc;
cache.getScriptedLocation(&script, &pc);
@ -2645,7 +2653,7 @@ IsPropertyAddInlineable(NativeObject *obj, HandleId id, ConstantOrRegister val,
static SetPropertyIC::NativeSetPropCacheability
CanAttachNativeSetProp(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegister val,
bool needsTypeBarrier, MutableHandleNativeObject holder,
bool needsTypeBarrier, MutableHandleObject holder,
MutableHandleShape shape, bool *checkTypeset)
{
if (!obj->isNative())
@ -2723,7 +2731,7 @@ SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
}
RootedShape shape(cx);
RootedNativeObject holder(cx);
RootedObject holder(cx);
bool checkTypeset;
canCache = CanAttachNativeSetProp(cx, obj, id, cache.value(), cache.needsTypeBarrier(),
&holder, &shape, &checkTypeset);

View File

@ -3047,79 +3047,73 @@ js::HasOwnProperty(JSContext *cx, HandleObject obj, HandleId id, bool *result)
return true;
}
static MOZ_ALWAYS_INLINE bool
LookupPropertyPureInline(ExclusiveContext *cx, JSObject *obj, jsid id, NativeObject **objp,
Shape **propp)
bool
js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **objp,
Shape **propp)
{
if (!obj->isNative())
return false;
do {
if (obj->isNative()) {
/* Search for a native dense element, typed array element, or property. */
NativeObject *current = &obj->as<NativeObject>();
while (true) {
/* Search for a native dense element, typed array element, or property. */
if (JSID_IS_INT(id) && obj->as<NativeObject>().containsDenseElement(JSID_TO_INT(id))) {
*objp = obj;
MarkDenseOrTypedArrayElementFound<NoGC>(propp);
return true;
}
if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) {
*objp = current;
MarkDenseOrTypedArrayElementFound<NoGC>(propp);
return true;
}
if (IsAnyTypedArray(current)) {
uint64_t index;
if (IsTypedArrayIndex(id, &index)) {
if (index < AnyTypedArrayLength(obj)) {
*objp = current;
MarkDenseOrTypedArrayElementFound<NoGC>(propp);
} else {
*objp = nullptr;
*propp = nullptr;
if (IsAnyTypedArray(obj)) {
uint64_t index;
if (IsTypedArrayIndex(id, &index)) {
if (index < AnyTypedArrayLength(obj)) {
*objp = obj;
MarkDenseOrTypedArrayElementFound<NoGC>(propp);
} else {
*objp = nullptr;
*propp = nullptr;
}
return true;
}
}
if (Shape *shape = obj->as<NativeObject>().lookupPure(id)) {
*objp = obj;
*propp = shape;
return true;
}
// Fail if there's a resolve hook. We allow the JSFunction resolve hook
// if we know it will never add a property with this name or str_resolve
// with a non-integer property.
do {
const Class *clasp = obj->getClass();
if (!clasp->resolve)
break;
if (clasp->resolve == fun_resolve && !FunctionHasResolveHook(cx->names(), id))
break;
if (clasp->resolve == str_resolve && !JSID_IS_INT(id))
break;
return false;
} while (0);
} else {
// Search for a property on an unboxed object. Other non-native objects
// are not handled here.
if (!obj->is<UnboxedPlainObject>())
return false;
if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
*objp = obj;
MarkNonNativePropertyFound<NoGC>(propp);
return true;
}
}
if (Shape *shape = current->lookupPure(id)) {
*objp = current;
*propp = shape;
return true;
}
// Fail if there's a resolve hook. We allow the JSFunction resolve hook
// if we know it will never add a property with this name or str_resolve
// with a non-integer property.
do {
const Class *clasp = current->getClass();
if (!clasp->resolve)
break;
if (clasp->resolve == fun_resolve && !FunctionHasResolveHook(cx->names(), id))
break;
if (clasp->resolve == str_resolve && !JSID_IS_INT(id))
break;
return false;
} while (0);
JSObject *proto = current->getProto();
if (!proto)
break;
if (!proto->isNative())
return false;
current = &proto->as<NativeObject>();
}
obj = obj->getProto();
} while (obj);
*objp = nullptr;
*propp = nullptr;
return true;
}
bool
js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, NativeObject **objp,
Shape **propp)
{
return LookupPropertyPureInline(cx, obj, id, objp, propp);
}
bool
JSObject::reportReadOnly(JSContext *cx, jsid id, unsigned report)
{

View File

@ -1226,7 +1226,7 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp);
namespace js {
bool
LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, NativeObject **objp,
LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **objp,
Shape **propp);
bool

View File

@ -559,7 +559,7 @@ js::proxy_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
return false;
if (found) {
MarkNonNativePropertyFound(propp);
MarkNonNativePropertyFound<CanGC>(propp);
objp.set(obj);
} else {
objp.set(nullptr);

View File

@ -332,6 +332,7 @@ NativeObject::setLastProperty(ExclusiveContext *cx, HandleNativeObject obj, Hand
MOZ_ASSERT(!shape->inDictionary());
MOZ_ASSERT(shape->compartment() == obj->compartment());
MOZ_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
MOZ_ASSERT(shape->getObjectClass() == obj->getClass());
size_t oldSpan = obj->lastProperty()->slotSpan();
size_t newSpan = shape->slotSpan();
@ -355,6 +356,7 @@ NativeObject::setLastPropertyShrinkFixedSlots(Shape *shape)
MOZ_ASSERT(!shape->inDictionary());
MOZ_ASSERT(shape->compartment() == compartment());
MOZ_ASSERT(lastProperty()->slotSpan() == shape->slotSpan());
MOZ_ASSERT(shape->getObjectClass() == getClass());
DebugOnly<size_t> oldFixed = numFixedSlots();
DebugOnly<size_t> newFixed = shape->numFixedSlots();
@ -381,6 +383,30 @@ NativeObject::setLastPropertyMakeNonNative(Shape *shape)
shape_ = shape;
}
/* static */ void
NativeObject::setLastPropertyMakeNative(ExclusiveContext *cx, HandleNativeObject obj,
HandleShape shape)
{
MOZ_ASSERT(obj->getClass()->isNative());
MOZ_ASSERT(!obj->lastProperty()->isNative());
MOZ_ASSERT(shape->isNative());
MOZ_ASSERT(!obj->inDictionaryMode());
MOZ_ASSERT(!shape->inDictionary());
MOZ_ASSERT(shape->compartment() == obj->compartment());
obj->shape_ = shape;
obj->slots_ = nullptr;
obj->elements_ = emptyObjectElements;
size_t oldSpan = shape->numFixedSlots();
size_t newSpan = shape->slotSpan();
// A failures at this point will leave the object as a mutant, and we
// can't recover.
if (oldSpan != newSpan && !updateSlotsForSpan(cx, obj, oldSpan, newSpan))
CrashAtUnhandlableOOM("NativeObject::setLastPropertyMakeNative");
}
/* static */ bool
NativeObject::setSlotSpan(ExclusiveContext *cx, HandleNativeObject obj, uint32_t span)
{

View File

@ -417,6 +417,12 @@ class NativeObject : public JSObject
// that are (temporarily) inconsistent.
void setLastPropertyMakeNonNative(Shape *shape);
// As for setLastProperty(), but changes the class associated with the
// object to a native one. The object's type has already been changed, and
// this brings the shape into sync with it.
static void setLastPropertyMakeNative(ExclusiveContext *cx, HandleNativeObject obj,
HandleShape shape);
protected:
#ifdef DEBUG
void checkShapeConsistency();

View File

@ -1495,8 +1495,9 @@ template<> struct RootKind<BaseShape *> : SpecificRootKind<BaseShape *, THING_RO
// properties of non-native objects, and dense elements for native objects.
// Use separate APIs for these two cases.
template <AllowGC allowGC>
static inline void
MarkNonNativePropertyFound(MutableHandleShape propp)
MarkNonNativePropertyFound(typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
{
propp.set(reinterpret_cast<Shape*>(1));
}

View File

@ -196,15 +196,11 @@ UnboxedPlainObject::convertToNative(JSContext *cx)
if (!SetClassAndProto(cx, obj, &PlainObject::class_, proto))
return false;
// Any failures after this point will leave the object as a mutant, and we
// can't recover.
RootedPlainObject nobj(cx, &obj->as<PlainObject>());
if (!nobj->setLastProperty(cx, nobj, shape))
CrashAtUnhandlableOOM("UnboxedPlainObject::convertToNative");
RootedNativeObject nobj(cx, &obj->as<PlainObject>());
NativeObject::setLastPropertyMakeNative(cx, nobj, shape);
for (size_t i = 0; i < values.length(); i++)
nobj->initSlot(i, values[i]);
nobj->initSlotUnchecked(i, values[i]);
return true;
}
@ -250,7 +246,7 @@ UnboxedPlainObject::obj_lookupGeneric(JSContext *cx, HandleObject obj,
MutableHandleShape propp)
{
if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
MarkNonNativePropertyFound(propp);
MarkNonNativePropertyFound<CanGC>(propp);
objp.set(obj);
return true;
}

View File

@ -28,6 +28,12 @@ UnboxedTypeSize(JSValueType type)
}
}
static inline bool
UnboxedTypeNeedsPreBarrier(JSValueType type)
{
return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT;
}
// Class describing the layout of an UnboxedPlainObject.
class UnboxedLayout
{