Bug 1066878 - Inline Object.create(x) in Ion. r=bhackett

This commit is contained in:
Jan de Mooij 2015-01-24 14:43:57 +01:00
Родитель 8fb96ecee4
Коммит 540219d598
17 изменённых файлов: 176 добавлений и 56 удалений

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

@ -622,6 +622,42 @@ obj_isPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
return true;
}
PlainObject *
js::ObjectCreateImpl(JSContext *cx, HandleObject proto, NewObjectKind newKind, HandleTypeObject type)
{
// Give the new object a small number of fixed slots, like we do for empty
// object literals ({}).
gc::AllocKind allocKind = GuessObjectGCKind(0);
if (!proto) {
// Object.create(null) is common, optimize it by using an allocation
// site specific TypeObject. Because GetTypeCallerInitObject is pretty
// slow, the caller can pass in the type if it's known and we use that
// instead.
RootedTypeObject ntype(cx, type);
if (!ntype) {
ntype = GetTypeCallerInitObject(cx, JSProto_Null);
if (!ntype)
return nullptr;
}
MOZ_ASSERT(!ntype->proto().toObjectOrNull());
return NewObjectWithType<PlainObject>(cx, ntype, cx->global(), allocKind,
newKind);
}
return NewObjectWithGivenProto<PlainObject>(cx, proto, cx->global(), allocKind, newKind);
}
PlainObject *
js::ObjectCreateWithTemplate(JSContext *cx, HandlePlainObject templateObj)
{
RootedObject proto(cx, templateObj->getProto());
RootedTypeObject type(cx, templateObj->type());
return ObjectCreateImpl(cx, proto, GenericObject, type);
}
/* ES5 15.2.3.5: Object.create(O [, Properties]) */
bool
js::obj_create(JSContext *cx, unsigned argc, Value *vp)
@ -633,8 +669,8 @@ js::obj_create(JSContext *cx, unsigned argc, Value *vp)
return false;
}
RootedValue v(cx, args[0]);
if (!v.isObjectOrNull()) {
if (!args[0].isObjectOrNull()) {
RootedValue v(cx, args[0]);
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
if (!bytes)
return false;
@ -644,13 +680,8 @@ js::obj_create(JSContext *cx, unsigned argc, Value *vp)
return false;
}
RootedObject proto(cx, v.toObjectOrNull());
/*
* Use the callee's global as the parent of the new object to avoid dynamic
* scoping (i.e., using the caller's global).
*/
RootedObject obj(cx, NewObjectWithGivenProto<PlainObject>(cx, proto, &args.callee().global()));
RootedObject proto(cx, args[0].toObjectOrNull());
RootedPlainObject obj(cx, ObjectCreateImpl(cx, proto));
if (!obj)
return false;

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

@ -23,6 +23,13 @@ obj_construct(JSContext *cx, unsigned argc, JS::Value *vp);
bool
obj_valueOf(JSContext *cx, unsigned argc, JS::Value *vp);
PlainObject *
ObjectCreateImpl(JSContext *cx, HandleObject proto, NewObjectKind newKind = GenericObject,
HandleTypeObject type = js::NullPtr());
PlainObject *
ObjectCreateWithTemplate(JSContext *cx, HandlePlainObject templateObj);
// Object methods exposed so they can be installed in the self-hosting global.
bool
obj_create(JSContext *cx, unsigned argc, JS::Value *vp);

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

@ -0,0 +1,25 @@
// Ensure Ion inlining of Object.create(x) tests the type of x
// matches the template object.
var P1 = {};
var P2 = {};
minorgc();
function f1() {
for (var i=0; i<100; i++) {
var P = (i & 1) ? P1 : P2;
var o = Object.create(P);
assertEq(Object.getPrototypeOf(o), P);
}
}
f1();
function f2() {
var arr = [null, Array];
for (var i=0; i<99; i++) {
var p = arr[(i / 50)|0];
var o = Object.create(p);
assertEq(Object.getPrototypeOf(o), p);
}
}
f2();

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

@ -8909,6 +8909,14 @@ GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc,
return true;
}
if (native == obj_create && args.length() == 1 && args[0].isObjectOrNull()) {
RootedObject proto(cx, args[0].toObjectOrNull());
res.set(ObjectCreateImpl(cx, proto, TenuredObject));
if (!res)
return false;
return true;
}
return true;
}

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

@ -4038,9 +4038,9 @@ class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator>
typedef JSObject *(*NewInitObjectFn)(JSContext *, HandlePlainObject);
static const VMFunction NewInitObjectInfo = FunctionInfo<NewInitObjectFn>(NewInitObject);
typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandlePlainObject);
static const VMFunction NewInitObjectWithClassPrototypeInfo =
FunctionInfo<NewInitObjectWithClassPrototypeFn>(NewInitObjectWithClassPrototype);
typedef PlainObject *(*ObjectCreateWithTemplateFn)(JSContext *, HandlePlainObject);
static const VMFunction ObjectCreateWithTemplateInfo =
FunctionInfo<ObjectCreateWithTemplateFn>(ObjectCreateWithTemplate);
void
CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
@ -4056,10 +4056,12 @@ CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
// that derives its class from its prototype instead of being
// JSObject::class_'d) from self-hosted code, we need a different init
// function.
if (lir->mir()->templateObjectIsClassPrototype())
callVM(NewInitObjectWithClassPrototypeInfo, lir);
else
if (lir->mir()->mode() == MNewObject::ObjectLiteral) {
callVM(NewInitObjectInfo, lir);
} else {
MOZ_ASSERT(lir->mir()->mode() == MNewObject::ObjectCreate);
callVM(ObjectCreateWithTemplateInfo, lir);
}
if (ReturnReg != objReg)
masm.movePtr(ReturnReg, objReg);

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

@ -6120,7 +6120,7 @@ IonBuilder::jsop_newobject()
templateObject->hasSingletonType()
? gc::TenuredHeap
: templateObject->type()->initialHeap(constraints()),
/* templateObjectIsClassPrototype = */ false);
MNewObject::ObjectLiteral);
current->add(ins);
current->push(ins);

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

@ -755,6 +755,9 @@ class IonBuilder
InliningStatus inlineRegExpExec(CallInfo &callInfo);
InliningStatus inlineRegExpTest(CallInfo &callInfo);
// Object natives.
InliningStatus inlineObjectCreate(CallInfo &callInfo);
// Atomics natives.
InliningStatus inlineAtomicsCompareExchange(CallInfo &callInfo);
InliningStatus inlineAtomicsLoad(CallInfo &callInfo);

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

@ -163,6 +163,10 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
if (native == regexp_test)
return inlineRegExpTest(callInfo);
// Object natives.
if (native == obj_create)
return inlineObjectCreate(callInfo);
// Array intrinsics.
if (native == intrinsic_UnsafePutElements)
return inlineUnsafePutElements(callInfo);
@ -1601,6 +1605,50 @@ IonBuilder::inlineSubstringKernel(CallInfo &callInfo)
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineObjectCreate(CallInfo &callInfo)
{
if (callInfo.argc() != 1 || callInfo.constructing())
return InliningStatus_NotInlined;
NativeObject *templateObject = inspector->getTemplateObjectForNative(pc, obj_create);
if (!templateObject)
return InliningStatus_NotInlined;
MOZ_ASSERT(templateObject->is<PlainObject>());
MOZ_ASSERT(!templateObject->hasSingletonType());
// Ensure the argument matches the template object's prototype.
MDefinition *arg = callInfo.getArg(0);
if (JSObject *proto = templateObject->getProto()) {
if (IsInsideNursery(proto))
return InliningStatus_NotInlined;
types::TemporaryTypeSet *types = arg->resultTypeSet();
if (!types || types->getSingleton() != proto)
return InliningStatus_NotInlined;
MOZ_ASSERT(types->getKnownMIRType() == MIRType_Object);
} else {
if (arg->type() != MIRType_Null)
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
current->add(templateConst);
MNewObject *ins = MNewObject::New(alloc(), constraints(), templateConst,
templateObject->type()->initialHeap(constraints()),
MNewObject::ObjectCreate);
current->add(ins);
current->push(ins);
if (!resumeAfter(ins))
return InliningStatus_Error;
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
{

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

@ -2793,17 +2793,21 @@ class MNewObject
: public MUnaryInstruction,
public NoTypePolicy::Data
{
public:
enum Mode { ObjectLiteral, ObjectCreate };
private:
gc::InitialHeap initialHeap_;
bool templateObjectIsClassPrototype_;
Mode mode_;
MNewObject(types::CompilerConstraintList *constraints, MConstant *templateConst,
gc::InitialHeap initialHeap, bool templateObjectIsClassPrototype)
gc::InitialHeap initialHeap, Mode mode)
: MUnaryInstruction(templateConst),
initialHeap_(initialHeap),
templateObjectIsClassPrototype_(templateObjectIsClassPrototype)
mode_(mode)
{
PlainObject *obj = templateObject();
MOZ_ASSERT_IF(templateObjectIsClassPrototype, !shouldUseVM());
MOZ_ASSERT_IF(mode != ObjectLiteral, !shouldUseVM());
setResultType(MIRType_Object);
if (!obj->hasSingletonType())
setResultTypeSet(MakeSingletonTypeSet(constraints, obj));
@ -2821,18 +2825,17 @@ class MNewObject
static MNewObject *New(TempAllocator &alloc, types::CompilerConstraintList *constraints,
MConstant *templateConst, gc::InitialHeap initialHeap,
bool templateObjectIsClassPrototype)
Mode mode)
{
return new(alloc) MNewObject(constraints, templateConst, initialHeap,
templateObjectIsClassPrototype);
return new(alloc) MNewObject(constraints, templateConst, initialHeap, mode);
}
// Returns true if the code generator should call through to the
// VM rather than the fast path.
bool shouldUseVM() const;
bool templateObjectIsClassPrototype() const {
return templateObjectIsClassPrototype_;
Mode mode() const {
return mode_;
}
PlainObject *templateObject() const {

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

@ -1156,13 +1156,14 @@ MNewObject::writeRecoverData(CompactBufferWriter &writer) const
{
MOZ_ASSERT(canRecoverOnBailout());
writer.writeUnsigned(uint32_t(RInstruction::Recover_NewObject));
writer.writeByte(templateObjectIsClassPrototype_);
MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
writer.writeByte(uint8_t(mode_));
return true;
}
RNewObject::RNewObject(CompactBufferReader &reader)
{
templateObjectIsClassPrototype_ = reader.readByte();
mode_ = MNewObject::Mode(reader.readByte());
}
bool
@ -1173,10 +1174,12 @@ RNewObject::recover(JSContext *cx, SnapshotIterator &iter) const
JSObject *resultObject = nullptr;
// See CodeGenerator::visitNewObjectVMCall
if (templateObjectIsClassPrototype_)
resultObject = NewInitObjectWithClassPrototype(cx, templateObject);
else
if (mode_ == MNewObject::ObjectLiteral) {
resultObject = NewInitObject(cx, templateObject);
} else {
MOZ_ASSERT(mode_ == MNewObject::ObjectCreate);
resultObject = ObjectCreateWithTemplate(cx, templateObject);
}
if (!resultObject)
return false;

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

@ -613,7 +613,7 @@ class RTruncateToInt32 MOZ_FINAL : public RInstruction
class RNewObject MOZ_FINAL : public RInstruction
{
private:
bool templateObjectIsClassPrototype_;
MNewObject::Mode mode_;
public:
RINSTRUCTION_HEADER_(NewObject)

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

@ -301,27 +301,6 @@ NewInitObject(JSContext *cx, HandlePlainObject templateObject)
return obj;
}
JSObject *
NewInitObjectWithClassPrototype(JSContext *cx, HandlePlainObject templateObject)
{
MOZ_ASSERT(!templateObject->hasSingletonType());
MOZ_ASSERT(!templateObject->hasLazyType());
NewObjectKind newKind = templateObject->type()->shouldPreTenure()
? TenuredObject
: GenericObject;
PlainObject *obj = NewObjectWithGivenProto<PlainObject>(cx,
templateObject->getProto(),
cx->global(),
newKind);
if (!obj)
return nullptr;
obj->setType(templateObject->type());
return obj;
}
bool
ArraySpliceDense(JSContext *cx, HandleObject obj, uint32_t start, uint32_t deleteCount)
{

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

@ -270,6 +270,7 @@ template <class> struct TypeToDataType { /* Unexpected return type for a VMFunct
template <> struct TypeToDataType<bool> { static const DataType result = Type_Bool; };
template <> struct TypeToDataType<JSObject *> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<NativeObject *> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<PlainObject *> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<InlineTypedObject *> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<DeclEnvObject *> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<ArrayObject *> { static const DataType result = Type_Object; };
@ -657,7 +658,6 @@ template<bool Equal>
bool StringsEqual(JSContext *cx, HandleString left, HandleString right, bool *res);
JSObject *NewInitObject(JSContext *cx, HandlePlainObject templateObject);
JSObject *NewInitObjectWithClassPrototype(JSContext *cx, HandlePlainObject templateObject);
bool ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval);
bool ArrayPushDense(JSContext *cx, HandleArrayObject obj, HandleValue v, uint32_t *length);

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

@ -2299,7 +2299,7 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
if (!res) {
RootedObject proto(cx);
if (!GetBuiltinPrototype(cx, key.kind, &proto))
if (key.kind != JSProto_Null && !GetBuiltinPrototype(cx, key.kind, &proto))
return nullptr;
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));

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

@ -309,6 +309,7 @@ inline const Class *
GetClassForProtoKey(JSProtoKey key)
{
switch (key) {
case JSProto_Null:
case JSProto_Object:
return &PlainObject::class_;
case JSProto_Array:
@ -369,7 +370,7 @@ inline TypeObject *
GetTypeNewObject(JSContext *cx, JSProtoKey key)
{
RootedObject proto(cx);
if (!GetBuiltinPrototype(cx, key, &proto))
if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto))
return nullptr;
return cx->getNewType(GetClassForProtoKey(key), TaggedProto(proto.get()));
}

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

@ -1422,7 +1422,8 @@ js::NewObjectWithTypeCommon(JSContext *cx, HandleTypeObject type, JSObject *pare
NewObjectCache &cache = cx->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (parent == type->proto().toObject()->getParent() &&
if (type->proto().isObject() &&
parent == type->proto().toObject()->getParent() &&
newKind == GenericObject &&
type->clasp()->isNative() &&
!cx->compartment()->hasObjectMetadataCallback())

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

@ -545,6 +545,15 @@ NewObjectWithGivenProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent,
return NewObjectWithGivenProto<T>(cx, TaggedProto(proto), parent, newKind);
}
template <typename T>
inline T *
NewObjectWithGivenProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent,
gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
{
JSObject *obj = NewObjectWithGivenProto(cx, &T::class_, TaggedProto(proto), parent, newKind);
return obj ? &obj->as<T>() : nullptr;
}
inline bool
FindProto(ExclusiveContext *cx, const js::Class *clasp, MutableHandleObject proto)
{