зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1184388 - Try to use uniform groups for multidimensional constant arrays and structures, r=jandem.
This commit is contained in:
Родитель
e13bcc74b8
Коммит
4cf7515fca
|
@ -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);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче