Bug 1184388 - Try to use uniform groups for multidimensional constant arrays and structures, r=jandem.

This commit is contained in:
Brian Hackett 2015-08-21 16:19:27 -06:00
Родитель e13bcc74b8
Коммит 4cf7515fca
10 изменённых файлов: 344 добавлений и 13 удалений

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

@ -4574,7 +4574,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs)
bool
ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects, MutableHandleValue vp,
NewObjectKind newKind)
Value* compare, size_t ncompare, NewObjectKind newKind)
{
MOZ_ASSERT(newKind == TenuredObject || newKind == SingletonObject);
@ -4625,7 +4625,7 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
return false;
size_t idx;
for (idx = 0; pn; idx++, pn = pn->pn_next) {
if (!pn->getConstantValue(cx, allowObjects, values[idx]))
if (!pn->getConstantValue(cx, allowObjects, values[idx], values.begin(), idx))
return false;
if (values[idx].isMagic(JS_GENERIC_MAGIC)) {
vp.setMagic(JS_GENERIC_MAGIC);
@ -4639,6 +4639,9 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
if (!obj)
return false;
if (!CombineArrayElementTypes(cx, obj, compare, ncompare))
return false;
vp.setObject(*obj);
return true;
}
@ -4685,6 +4688,9 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
if (!obj)
return false;
if (!CombinePlainObjectPropertyTypes(cx, obj, compare, ncompare))
return false;
vp.setObject(*obj);
return true;
}
@ -4700,7 +4706,7 @@ BytecodeEmitter::emitSingletonInitialiser(ParseNode* pn)
NewObjectKind newKind = (pn->getKind() == PNK_OBJECT) ? SingletonObject : TenuredObject;
RootedValue value(cx);
if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value, newKind))
if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value, nullptr, 0, newKind))
return false;
MOZ_ASSERT_IF(newKind == SingletonObject, value.toObject().isSingleton());

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

@ -916,6 +916,7 @@ class ParseNode
};
bool getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects, MutableHandleValue vp,
Value* compare = nullptr, size_t ncompare = 0,
NewObjectKind newKind = TenuredObject);
inline bool isConstant();

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

@ -0,0 +1,50 @@
function processNoProperty(a) {
var total = 0;
for (var i = 0; i < a.length; i++) {
var sa = a[i];
for (var j = 0; j < sa.length; j++)
total += sa[j];
}
assertEq(total, 22);
}
var literalArray = [
[1,2,3,4],
[1.5,2.5,3.5,4.5]
];
var jsonArray = JSON.parse(`[
[1,2,3,4],
[1.5,2.5,3.5,4.5]
]`);
for (var i = 0; i < 1000; i++) {
processNoProperty(literalArray);
processNoProperty(jsonArray);
}
function processWithProperty(a) {
var total = 0;
for (var i = 0; i < a.length; i++) {
var sa = a[i].p;
for (var j = 0; j < sa.length; j++)
total += sa[j];
}
assertEq(total, 22);
}
var literalPropertyArray = [
{p:[1,2,3,4]},
{p:[1.5,2.5,3.5,4.5]}
];
var jsonPropertyArray = JSON.parse(`[
{"p":[1,2,3,4]},
{"p":[1.5,2.5,3.5,4.5]}
]`);
for (var i = 0; i < 1000; i++) {
processWithProperty(literalPropertyArray);
processWithProperty(jsonPropertyArray);
}

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

@ -590,6 +590,13 @@ JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector& properties)
if (!freeProperties.append(&properties))
return false;
stack.popBack();
if (!stack.empty() && stack.back().state == FinishArrayElement) {
const ElementVector& elements = stack.back().elements();
if (!CombinePlainObjectPropertyTypes(cx, obj, elements.begin(), elements.length()))
return false;
}
return true;
}
@ -607,6 +614,13 @@ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements)
if (!freeElements.append(&elements))
return false;
stack.popBack();
if (!stack.empty() && stack.back().state == FinishArrayElement) {
const ElementVector& elements = stack.back().elements();
if (!CombineArrayElementTypes(cx, obj, elements.begin(), elements.length()))
return false;
}
return true;
}

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

@ -868,6 +868,206 @@ ObjectGroup::newArrayObject(ExclusiveContext* cx,
ShouldUpdateTypes::DontUpdate);
}
static bool
GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target)
{
MOZ_ASSERT(source->group() != target->group());
if (!target->is<ArrayObject>() && !target->is<UnboxedArrayObject>())
return true;
if (target->group()->maybePreliminaryObjects()) {
bool force = IsInsideNursery(source);
target->group()->maybePreliminaryObjects()->maybeAnalyze(cx, target->group(), force);
}
if (target->is<ArrayObject>()) {
ObjectGroup* sourceGroup = source->group();
if (source->is<UnboxedArrayObject>()) {
Shape* shape = target->as<ArrayObject>().lastProperty();
if (!UnboxedArrayObject::convertToNativeWithGroup(cx, source, target->group(), shape))
return false;
} else if (source->is<ArrayObject>()) {
source->setGroup(target->group());
} else {
return true;
}
if (sourceGroup->maybePreliminaryObjects())
sourceGroup->maybePreliminaryObjects()->unregisterObject(source);
if (target->group()->maybePreliminaryObjects())
target->group()->maybePreliminaryObjects()->registerNewObject(source);
for (size_t i = 0; i < source->as<ArrayObject>().getDenseInitializedLength(); i++) {
Value v = source->as<ArrayObject>().getDenseElement(i);
AddTypePropertyId(cx, source->group(), source, JSID_VOID, v);
}
return true;
}
if (target->is<UnboxedArrayObject>()) {
if (!source->is<UnboxedArrayObject>())
return true;
if (source->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_INT32)
return true;
if (target->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_DOUBLE)
return true;
return source->as<UnboxedArrayObject>().convertInt32ToDouble(cx, target->group());
}
return true;
}
static bool
SameGroup(JSObject* first, JSObject* second)
{
return first->group() == second->group();
}
// When generating a multidimensional array of literals, such as
// [[1,2],[3,4],[5.5,6.5]], try to ensure that each element of the array has
// the same group. This is mainly important when the elements might have
// different native vs. unboxed layouts, or different unboxed layouts, and
// accessing the heterogenous layouts from JIT code will be much slower than
// if they were homogenous.
//
// To do this, with each new array element we compare it with one of the
// previous ones, and try to mutate the group of the new element to fit that
// of the old element. If this isn't possible, the groups for all old elements
// are mutated to fit that of the new element.
bool
js::CombineArrayElementTypes(ExclusiveContext* cx, JSObject* newObj,
const Value* compare, size_t ncompare)
{
if (!ncompare || !compare[0].isObject())
return true;
JSObject* oldObj = &compare[0].toObject();
if (SameGroup(oldObj, newObj))
return true;
if (!GiveObjectGroup(cx, oldObj, newObj))
return false;
if (SameGroup(oldObj, newObj))
return true;
if (!GiveObjectGroup(cx, newObj, oldObj))
return false;
if (SameGroup(oldObj, newObj)) {
for (size_t i = 1; i < ncompare; i++) {
if (compare[i].isObject() && !SameGroup(&compare[i].toObject(), newObj)) {
if (!GiveObjectGroup(cx, newObj, &compare[i].toObject()))
return false;
}
}
}
return true;
}
// Similarly to CombineArrayElementTypes, if we are generating an array of
// plain objects with a consistent property layout, such as
// [{p:[1,2]},{p:[3,4]},{p:[5.5,6.5]}], where those plain objects in
// turn have arrays as their own properties, try to ensure that a consistent
// group is given to each array held by the same property of the plain objects.
bool
js::CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj,
const Value* compare, size_t ncompare)
{
if (!ncompare || !compare[0].isObject())
return true;
JSObject* oldObj = &compare[0].toObject();
if (!SameGroup(oldObj, newObj))
return true;
if (newObj->is<PlainObject>()) {
MOZ_ASSERT(newObj->as<PlainObject>().lastProperty() == oldObj->as<PlainObject>().lastProperty());
for (size_t slot = 0; slot < newObj->as<PlainObject>().slotSpan(); slot++) {
Value newValue = newObj->as<PlainObject>().getSlot(slot);
Value oldValue = oldObj->as<PlainObject>().getSlot(slot);
if (!newValue.isObject() || !oldValue.isObject())
continue;
JSObject* newInnerObj = &newValue.toObject();
JSObject* oldInnerObj = &oldValue.toObject();
if (SameGroup(oldInnerObj, newInnerObj))
continue;
if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj))
return false;
if (SameGroup(oldInnerObj, newInnerObj))
continue;
if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj))
return false;
if (SameGroup(oldInnerObj, newInnerObj)) {
for (size_t i = 1; i < ncompare; i++) {
if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) {
Value otherValue = compare[i].toObject().as<PlainObject>().getSlot(slot);
if (otherValue.isObject() && !SameGroup(&otherValue.toObject(), newInnerObj)) {
if (!GiveObjectGroup(cx, newInnerObj, &otherValue.toObject()))
return false;
}
}
}
}
}
} else if (newObj->is<UnboxedPlainObject>()) {
const UnboxedLayout& layout = newObj->as<UnboxedPlainObject>().layout();
const int32_t* traceList = layout.traceList();
if (!traceList)
return true;
uint8_t* newData = newObj->as<UnboxedPlainObject>().data();
uint8_t* oldData = oldObj->as<UnboxedPlainObject>().data();
for (; *traceList != -1; traceList++) {}
traceList++;
for (; *traceList != -1; traceList++) {
JSObject* newInnerObj = *reinterpret_cast<JSObject**>(newData + *traceList);
JSObject* oldInnerObj = *reinterpret_cast<JSObject**>(oldData + *traceList);
if (!newInnerObj || !oldInnerObj || SameGroup(oldInnerObj, newInnerObj))
continue;
if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj))
return false;
if (SameGroup(oldInnerObj, newInnerObj))
continue;
if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj))
return false;
if (SameGroup(oldInnerObj, newInnerObj)) {
for (size_t i = 1; i < ncompare; i++) {
if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) {
uint8_t* otherData = compare[i].toObject().as<UnboxedPlainObject>().data();
JSObject* otherInnerObj = *reinterpret_cast<JSObject**>(otherData + *traceList);
if (otherInnerObj && !SameGroup(otherInnerObj, newInnerObj)) {
if (!GiveObjectGroup(cx, newInnerObj, otherInnerObj))
return false;
}
}
}
}
}
}
return true;
}
/////////////////////////////////////////////////////////////////////
// ObjectGroupCompartment PlainObjectTable
/////////////////////////////////////////////////////////////////////

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

@ -720,6 +720,14 @@ PlainObject*
NewPlainObjectWithProperties(ExclusiveContext* cx, IdValuePair* properties, size_t nproperties,
NewObjectKind newKind);
bool
CombineArrayElementTypes(ExclusiveContext* cx, JSObject* newObj,
const Value* compare, size_t ncompare);
bool
CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj,
const Value* compare, size_t ncompare);
} // namespace js
#endif /* vm_ObjectGroup_h */

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

@ -3312,6 +3312,19 @@ PreliminaryObjectArray::registerNewObject(JSObject* res)
MOZ_CRASH("There should be room for registering the new object");
}
void
PreliminaryObjectArray::unregisterObject(JSObject* obj)
{
for (size_t i = 0; i < COUNT; i++) {
if (objects[i] == obj) {
objects[i] = nullptr;
return;
}
}
MOZ_CRASH("The object should be in the array");
}
bool
PreliminaryObjectArray::full() const
{

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

@ -794,6 +794,7 @@ class PreliminaryObjectArray
}
void registerNewObject(JSObject* res);
void unregisterObject(JSObject* obj);
JSObject* get(size_t i) const {
MOZ_ASSERT(i < COUNT);

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

@ -979,15 +979,9 @@ DefineBoxedOrUnboxedFunctor3(AppendUnboxedDenseElements,
UnboxedArrayObject*, uint32_t, AutoValueVector*);
/* static */ bool
UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
UnboxedArrayObject::convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
ObjectGroup* group, Shape* shape)
{
const UnboxedLayout& layout = obj->as<UnboxedArrayObject>().layout();
if (!layout.nativeGroup()) {
if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
return false;
}
size_t length = obj->as<UnboxedArrayObject>().length();
size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
@ -999,10 +993,10 @@ UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
DebugOnly<DenseElementResult> result = CallBoxedOrUnboxedSpecialization(functor, obj);
MOZ_ASSERT(result.value == DenseElementResult::Success);
obj->setGroup(layout.nativeGroup());
obj->setGroup(group);
ArrayObject* aobj = &obj->as<ArrayObject>();
aobj->setLastPropertyMakeNative(cx, layout.nativeShape());
aobj->setLastPropertyMakeNative(cx, shape);
// Make sure there is at least one element, so that this array does not
// use emptyObjectElements.
@ -1017,6 +1011,46 @@ UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
return true;
}
/* static */ bool
UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
{
const UnboxedLayout& layout = obj->as<UnboxedArrayObject>().layout();
if (!layout.nativeGroup()) {
if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
return false;
}
return convertToNativeWithGroup(cx, obj, layout.nativeGroup(), layout.nativeShape());
}
bool
UnboxedArrayObject::convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group)
{
MOZ_ASSERT(elementType() == JSVAL_TYPE_INT32);
MOZ_ASSERT(group->unboxedLayout().elementType() == JSVAL_TYPE_DOUBLE);
Vector<int32_t> values(cx);
if (!values.reserve(initializedLength()))
return false;
for (size_t i = 0; i < initializedLength(); i++)
values.infallibleAppend(getElementSpecific<JSVAL_TYPE_INT32>(i).toInt32());
uint8_t* newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
capacity() * sizeof(int32_t),
capacity() * sizeof(double));
if (!newElements)
return false;
setGroup(group);
elements_ = newElements;
for (size_t i = 0; i < initializedLength(); i++)
setElementNoTypeChangeSpecific<JSVAL_TYPE_DOUBLE>(i, DoubleValue(values[i]));
return true;
}
/* static */ UnboxedArrayObject*
UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length,
NewObjectKind newKind, uint32_t maxLength)

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

@ -421,6 +421,10 @@ class UnboxedArrayObject : public JSObject
uint32_t length, NewObjectKind newKind,
uint32_t maxLength = MaximumCapacity);
static bool convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj,
ObjectGroup* group, Shape* shape);
bool convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group);
void fillAfterConvert(ExclusiveContext* cx,
const AutoValueVector& values, size_t* valueCursor);