Specialize big typed arrays with singleton types, bug 762561. r=dvander

This commit is contained in:
Brian Hackett 2012-06-26 17:47:20 -07:00
parent 947d4ffd7e
commit 3a5a434a81
9 changed files with 132 additions and 83 deletions

View File

@ -771,6 +771,8 @@ TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter)
static inline const Shape *
GetSingletonShape(JSContext *cx, JSObject *obj, jsid id)
{
if (!obj->isNative())
return NULL;
const Shape *shape = obj->nativeLookup(cx, id);
if (shape && shape->hasDefaultGetter() && shape->hasSlot())
return shape;
@ -1932,27 +1934,26 @@ types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc)
}
bool
types::UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc)
types::UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey key)
{
/*
* Objects created outside loops in global and eval scripts should have
* singleton types. For now this is only done for plain objects, not arrays.
* singleton types. For now this is only done for plain objects and typed
* arrays, but not normal arrays.
*/
if (!cx->typeInferenceEnabled() || script->function())
return false;
JSOp op = JSOp(*pc);
if (op == JSOP_NEWOBJECT || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Object)) {
AutoEnterTypeInference enter(cx);
if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
return false;
if (!script->ensureRanAnalysis(cx, NULL))
return false;
AutoEnterTypeInference enter(cx);
return !script->analysis()->getCode(pc).inLoop;
}
if (!script->ensureRanAnalysis(cx, NULL))
return false;
return false;
return !script->analysis()->getCode(pc).inLoop;
}
bool
@ -2701,7 +2702,7 @@ TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop)
UpdatePropertyType(cx, &base->types, singleton, shape, true);
shape = shape->previous();
}
} else if (!JSID_IS_EMPTY(id)) {
} else if (!JSID_IS_EMPTY(id) && singleton->isNative()) {
const Shape *shape = singleton->nativeLookup(cx, id);
if (shape)
UpdatePropertyType(cx, &base->types, singleton, shape, false);
@ -3110,14 +3111,16 @@ GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc)
if (!script->hasGlobal())
return NULL;
if (UseNewTypeForInitializer(cx, script, pc))
return NULL;
JSOp op = JSOp(*pc);
JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT);
bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array));
return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object);
JSProtoKey key = isArray ? JSProto_Array : JSProto_Object;
if (UseNewTypeForInitializer(cx, script, pc, key))
return NULL;
return TypeScript::InitObject(cx, script, pc, key);
}
/*
@ -3756,7 +3759,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
TypeSet *types = script->analysis()->bytecodeTypes(pc);
types->addSubset(cx, &pushed[0]);
if (UseNewTypeForInitializer(cx, script, pc)) {
bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array));
JSProtoKey key = isArray ? JSProto_Array : JSProto_Object;
if (UseNewTypeForInitializer(cx, script, pc, key)) {
/* Defer types pushed by this bytecode until runtime. */
break;
}
@ -5534,8 +5540,8 @@ JSObject::makeLazyType(JSContext *cx)
{
JS_ASSERT(hasLazyType());
TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
JSProto_Object, getProto());
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(getClass());
TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, key, getProto());
if (!type) {
if (cx->typeInferenceEnabled())
cx->compartment->types.setPendingNukeTypes(cx);
@ -5578,16 +5584,6 @@ JSObject::makeLazyType(JSContext *cx)
if (getClass()->ext.equality)
type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
if (type->unknownProperties()) {
type_ = type;
return;
}
/* Not yet generating singleton arrays. */
type->flags |= OBJECT_FLAG_NON_DENSE_ARRAY
| OBJECT_FLAG_NON_PACKED_ARRAY
| OBJECT_FLAG_NON_TYPED_ARRAY;
type_ = type;
}

View File

@ -871,7 +871,7 @@ UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc);
/* Whether to use a new type object for an initializer opcode at script/pc. */
bool
UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc);
UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey key);
/*
* Whether Array.prototype, or an object on its proto chain, has an

View File

@ -531,7 +531,7 @@ struct AllocationSiteKey {
/* static */ inline TypeObject *
TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind)
{
JS_ASSERT(!UseNewTypeForInitializer(cx, script, pc));
JS_ASSERT(!UseNewTypeForInitializer(cx, script, pc, kind));
/* :XXX: Limit script->length so we don't need to check the offset up front? */
uint32_t offset = pc - script->code;
@ -561,7 +561,10 @@ SetInitializerObjectType(JSContext *cx, JSScript *script, jsbytecode *pc, JSObje
if (!cx->typeInferenceEnabled())
return true;
if (UseNewTypeForInitializer(cx, script, pc)) {
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
JS_ASSERT(key != JSProto_Null);
if (UseNewTypeForInitializer(cx, script, pc, key)) {
if (!obj->setSingletonType(cx))
return false;
@ -572,7 +575,6 @@ SetInitializerObjectType(JSContext *cx, JSScript *script, jsbytecode *pc, JSObje
*/
TypeScript::Monitor(cx, script, pc, ObjectValue(*obj));
} else {
JSProtoKey key = obj->isDenseArray() ? JSProto_Array : JSProto_Object;
types::TypeObject *type = TypeScript::InitObject(cx, script, pc, key);
if (!type)
return false;
@ -1333,35 +1335,29 @@ TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
TypeObjectFlags flags = 0;
switch (key) {
case JSProto_Function:
JS_ASSERT(isFunction());
/* FALLTHROUGH */
case JSProto_Object:
flags = OBJECT_FLAG_NON_DENSE_ARRAY
| OBJECT_FLAG_NON_PACKED_ARRAY
| OBJECT_FLAG_NON_TYPED_ARRAY;
break;
case JSProto_Array:
flags = OBJECT_FLAG_NON_TYPED_ARRAY;
break;
default:
/* :XXX: abstract */
JS_ASSERT(key == JSProto_Int8Array ||
key == JSProto_Uint8Array ||
key == JSProto_Int16Array ||
key == JSProto_Uint16Array ||
key == JSProto_Int32Array ||
key == JSProto_Uint32Array ||
key == JSProto_Float32Array ||
key == JSProto_Float64Array ||
key == JSProto_Uint8ClampedArray ||
key == JSProto_DataView);
case JSProto_Int8Array:
case JSProto_Uint8Array:
case JSProto_Int16Array:
case JSProto_Uint16Array:
case JSProto_Int32Array:
case JSProto_Uint32Array:
case JSProto_Float32Array:
case JSProto_Float64Array:
case JSProto_Uint8ClampedArray:
case JSProto_DataView:
flags = OBJECT_FLAG_NON_DENSE_ARRAY
| OBJECT_FLAG_NON_PACKED_ARRAY;
break;
default:
flags = OBJECT_FLAG_NON_DENSE_ARRAY
| OBJECT_FLAG_NON_PACKED_ARRAY
| OBJECT_FLAG_NON_TYPED_ARRAY;
break;
}
if (!hasAllFlags(flags))

View File

@ -2786,10 +2786,13 @@ js_Object(JSContext *cx, unsigned argc, Value *vp)
obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
if (!obj)
return JS_FALSE;
TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Object);
if (!type)
return JS_FALSE;
obj->setType(type);
jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc);
if (script) {
/* Try to specialize the type of the object to the scripted call site. */
if (!types::SetInitializerObjectType(cx, script, pc, obj))
return JS_FALSE;
}
}
vp->setObject(*obj);
return JS_TRUE;

View File

@ -1414,20 +1414,24 @@ class TypedArrayTemplate
return NULL;
JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT8_BACKGROUND);
types::TypeObject *type;
if (proto) {
type = proto->getNewType(cx);
} else {
/*
* Specialize the type of the object on the current scripted location,
* and mark the type as definitely a typed array.
*/
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(protoClass());
type = types::GetTypeCallerInitObject(cx, key);
types::TypeObject *type = proto->getNewType(cx);
if (!type)
return NULL;
obj->setType(type);
} else if (cx->typeInferenceEnabled()) {
if (len * sizeof(NativeType) >= TypedArray::SINGLETON_TYPE_BYTE_LENGTH) {
if (!obj->setSingletonType(cx))
return NULL;
} else {
jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc);
if (script) {
if (!types::SetInitializerObjectType(cx, script, pc, obj))
return NULL;
}
}
}
obj->setType(type);
obj->setSlot(FIELD_TYPE, Int32Value(ArrayTypeID()));
obj->setSlot(FIELD_BUFFER, ObjectValue(*bufobj));

View File

@ -251,6 +251,12 @@ struct TypedArray {
return slotWidth(getType(obj));
}
/*
* Byte length above which created typed arrays and data views will have
* singleton types regardless of the context in which they are created.
*/
static const uint32_t SINGLETON_TYPE_BYTE_LENGTH = 1024 * 1024 * 10;
static int lengthOffset();
static int dataOffset();
};

View File

@ -105,20 +105,24 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
if (!obj)
return NULL;
types::TypeObject *type;
if (proto) {
type = proto->getNewType(cx);
} else {
/*
* Specialize the type of the object on the current scripted location,
* and mark the type as definitely a data view
*/
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(&DataViewClass);
type = types::GetTypeCallerInitObject(cx, key);
types::TypeObject *type = proto->getNewType(cx);
if (!type)
return NULL;
obj->setType(type);
} else if (cx->typeInferenceEnabled()) {
if (byteLength >= TypedArray::SINGLETON_TYPE_BYTE_LENGTH) {
if (!obj->setSingletonType(cx))
return NULL;
} else {
jsbytecode *pc;
JSScript *script = cx->stack.currentScript(&pc);
if (script) {
if (!types::SetInitializerObjectType(cx, script, pc, obj))
return NULL;
}
}
}
obj->setType(type);
JS_ASSERT(arrayBuffer->isArrayBuffer());

View File

@ -4784,6 +4784,13 @@ mjit::Compiler::jsop_getprop(PropertyName *name, JSValueType knownType,
* The typed array length always fits in an int32.
*/
if (!types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_TYPED_ARRAY)) {
if (top->isConstant()) {
JSObject *obj = &top->getValue().toObject();
uint32_t length = TypedArray::getLength(obj);
frame.pop();
frame.push(Int32Value(length));
return true;
}
bool isObject = top->isTypeKnown();
if (!isObject) {
Jump notObject = frame.testObject(Assembler::NotEqual, top);
@ -6684,14 +6691,15 @@ mjit::Compiler::jsop_newinit()
stubArg = (void *) baseobj;
}
JSProtoKey key = isArray ? JSProto_Array : JSProto_Object;
/*
* Don't bake in types for non-compileAndGo scripts, or at initializers
* producing objects with singleton types.
*/
RootedTypeObject type(cx);
if (globalObj && !types::UseNewTypeForInitializer(cx, script, PC)) {
type = types::TypeScript::InitObject(cx, script, PC,
isArray ? JSProto_Array : JSProto_Object);
if (globalObj && !types::UseNewTypeForInitializer(cx, script, PC, key)) {
type = types::TypeScript::InitObject(cx, script, PC, key);
if (!type)
return false;
}

View File

@ -1427,6 +1427,22 @@ mjit::Compiler::jsop_setelem_typed(int atype)
FrameEntry *slotsFe = loop->invariantArraySlots(objv);
objReg = frame.tempRegForData(slotsFe);
frame.pinReg(objReg);
} else if (obj->isConstant()) {
JSObject *array = &obj->getValue().toObject();
int32_t length = (int32_t) TypedArray::getLength(array);
void *data = TypedArray::getDataOffset(array);
objReg = frame.allocReg();
if (key.isConstant()) {
if (key.index() >= length)
stubcc.linkExit(masm.jump(), Uses(2));
} else {
Jump lengthGuard = masm.branch32(Assembler::AboveOrEqual, key.reg(), Imm32(length));
stubcc.linkExit(lengthGuard, Uses(2));
}
masm.move(ImmPtr(data), objReg);
} else {
objReg = frame.copyDataIntoReg(obj);
@ -1519,8 +1535,6 @@ mjit::Compiler::jsop_setelem(bool popGuaranteed)
return true;
}
frame.forgetMismatchedObject(obj);
// If the object is definitely a dense array or a typed array we can generate
// code directly without using an inline cache.
if (cx->typeInferenceEnabled()) {
@ -1546,6 +1560,8 @@ mjit::Compiler::jsop_setelem(bool popGuaranteed)
#endif
}
frame.forgetMismatchedObject(obj);
if (id->isType(JSVAL_TYPE_DOUBLE) || !globalObj) {
jsop_setelem_slow();
return true;
@ -2011,6 +2027,22 @@ mjit::Compiler::jsop_getelem_typed(int atype)
FrameEntry *slotsFe = loop->invariantArraySlots(objv);
objReg = frame.tempRegForData(slotsFe);
frame.pinReg(objReg);
} else if (obj->isConstant()) {
JSObject *array = &obj->getValue().toObject();
int32_t length = (int32_t) TypedArray::getLength(array);
void *data = TypedArray::getDataOffset(array);
objReg = frame.allocReg();
if (key.isConstant()) {
if (key.index() >= length)
stubcc.linkExit(masm.jump(), Uses(2));
} else {
Jump lengthGuard = masm.branch32(Assembler::AboveOrEqual, key.reg(), Imm32(length));
stubcc.linkExit(lengthGuard, Uses(2));
}
masm.move(ImmPtr(data), objReg);
} else {
objReg = frame.copyDataIntoReg(obj);