mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Add Array.concat stub for concatenating known dense arrays, bug 692960. r=dvander
This commit is contained in:
parent
9c5cfe112f
commit
b001b60a02
18
js/src/jit-test/tests/basic/arrayConcat.js
Normal file
18
js/src/jit-test/tests/basic/arrayConcat.js
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
/* Test concat compiler paths. */
|
||||
|
||||
for (var i = 9; i < 10; i++)
|
||||
assertEq([2].concat([3])[0], 2);
|
||||
|
||||
function f(a, b) {
|
||||
return a.concat(b)[0];
|
||||
}
|
||||
function g() {
|
||||
var x = [];
|
||||
var y = [1];
|
||||
for (var i = 0; i < 50; i++)
|
||||
assertEq(f(x, y), 1);
|
||||
eval('y[0] = "three"');
|
||||
assertEq(f(x, y), "three");
|
||||
}
|
||||
g();
|
@ -2940,11 +2940,41 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
void JS_FASTCALL
|
||||
mjit::stubs::ArrayConcatTwoArrays(VMFrame &f)
|
||||
{
|
||||
JSObject *result = &f.regs.sp[-3].toObject();
|
||||
JSObject *obj1 = &f.regs.sp[-2].toObject();
|
||||
JSObject *obj2 = &f.regs.sp[-1].toObject();
|
||||
|
||||
JS_ASSERT(result->isDenseArray() && obj1->isDenseArray() && obj2->isDenseArray());
|
||||
|
||||
uint32 initlen1 = obj1->getDenseArrayInitializedLength();
|
||||
JS_ASSERT(initlen1 == obj1->getArrayLength());
|
||||
|
||||
uint32 initlen2 = obj2->getDenseArrayInitializedLength();
|
||||
JS_ASSERT(initlen2 == obj2->getArrayLength());
|
||||
|
||||
/* No overflow here due to nslots limit. */
|
||||
uint32 len = initlen1 + initlen2;
|
||||
|
||||
if (!result->ensureSlots(f.cx, len))
|
||||
THROW();
|
||||
|
||||
result->copyDenseArrayElements(0, obj1->getDenseArrayElements(), initlen1);
|
||||
result->copyDenseArrayElements(initlen1, obj2->getDenseArrayElements(), initlen2);
|
||||
|
||||
result->setDenseArrayInitializedLength(len);
|
||||
result->setDenseArrayLength(len);
|
||||
}
|
||||
#endif /* JS_METHODJIT */
|
||||
|
||||
/*
|
||||
* Python-esque sequence operations.
|
||||
*/
|
||||
static JSBool
|
||||
array_concat(JSContext *cx, uintN argc, Value *vp)
|
||||
JSBool
|
||||
js::array_concat(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
/* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
|
||||
Value *p = JS_ARGV(cx, vp) - 1;
|
||||
|
@ -227,6 +227,9 @@ array_push(JSContext *cx, uintN argc, js::Value *vp);
|
||||
extern JSBool
|
||||
array_pop(JSContext *cx, uintN argc, js::Value *vp);
|
||||
|
||||
extern JSBool
|
||||
array_concat(JSContext *cx, uintN argc, js::Value *vp);
|
||||
|
||||
extern JSBool
|
||||
array_shift(JSContext *cx, uintN argc, js::Value *vp);
|
||||
|
||||
|
@ -1771,12 +1771,34 @@ TypeSet::knownNonEmpty(JSContext *cx)
|
||||
if (baseFlags() != 0 || baseObjectCount() != 0)
|
||||
return true;
|
||||
|
||||
add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreeze>(
|
||||
cx->compartment->types.compiledScript), false);
|
||||
addFreeze(cx);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeSet::knownSubset(JSContext *cx, TypeSet *other)
|
||||
{
|
||||
if ((baseFlags() & other->baseFlags()) != baseFlags())
|
||||
return false;
|
||||
|
||||
if (unknownObject()) {
|
||||
JS_ASSERT(other->unknownObject());
|
||||
} else {
|
||||
for (unsigned i = 0; i < getObjectCount(); i++) {
|
||||
TypeObjectKey *obj = getObject(i);
|
||||
if (!obj)
|
||||
continue;
|
||||
if (!other->hasType(Type::ObjectType(obj)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
addFreeze(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
TypeSet::getTypedArrayType(JSContext *cx)
|
||||
{
|
||||
|
@ -491,6 +491,9 @@ class TypeSet
|
||||
/* Get whether this type set is non-empty. */
|
||||
bool knownNonEmpty(JSContext *cx);
|
||||
|
||||
/* Get whether this type set is known to be a subset of other. */
|
||||
bool knownSubset(JSContext *cx, TypeSet *other);
|
||||
|
||||
/*
|
||||
* Get the typed array type of all objects in this set. Returns
|
||||
* TypedArray::TYPE_MAX if the set contains different array types.
|
||||
|
@ -962,8 +962,9 @@ TypeSet::addType(JSContext *cx, Type type)
|
||||
return;
|
||||
|
||||
if (type.isUnknown()) {
|
||||
flags = TYPE_FLAG_UNKNOWN | (flags & ~baseFlags());
|
||||
flags |= TYPE_FLAG_BASE_MASK;
|
||||
clearObjects();
|
||||
JS_ASSERT(unknown());
|
||||
} else if (type.isPrimitive()) {
|
||||
TypeFlags flag = PrimitiveTypeFlag(type.primitive());
|
||||
if (flags & flag)
|
||||
|
@ -524,7 +524,8 @@ inline void
|
||||
JSObject::copyDenseArrayElements(uintN dstStart, const js::Value *src, uintN count)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
copySlotRange(dstStart, src, count);
|
||||
JS_ASSERT(dstStart + count <= capacity);
|
||||
memcpy(slots + dstStart, src, count * sizeof(js::Value));
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -780,6 +780,8 @@ private:
|
||||
Assembler::Condition cond);
|
||||
CompileStatus compileMathPowSimple(FrameEntry *arg1, FrameEntry *arg2);
|
||||
CompileStatus compileArrayPush(FrameEntry *thisv, FrameEntry *arg);
|
||||
CompileStatus compileArrayConcat(types::TypeSet *thisTypes, types::TypeSet *argTypes,
|
||||
FrameEntry *thisValue, FrameEntry *argValue);
|
||||
CompileStatus compileArrayPopShift(FrameEntry *thisv, bool isPacked, bool isArrayPop);
|
||||
CompileStatus compileArrayWithLength(uint32 argc);
|
||||
CompileStatus compileArrayWithArgs(uint32 argc);
|
||||
|
@ -589,6 +589,94 @@ mjit::Compiler::compileArrayPopShift(FrameEntry *thisValue, bool isPacked, bool
|
||||
return Compile_Okay;
|
||||
}
|
||||
|
||||
CompileStatus
|
||||
mjit::Compiler::compileArrayConcat(types::TypeSet *thisTypes, types::TypeSet *argTypes,
|
||||
FrameEntry *thisValue, FrameEntry *argValue)
|
||||
{
|
||||
/*
|
||||
* Require the 'this' types to have a specific type matching the current
|
||||
* global, so we can create the result object inline.
|
||||
*/
|
||||
if (thisTypes->getObjectCount() != 1)
|
||||
return Compile_InlineAbort;
|
||||
types::TypeObject *thisType = thisTypes->getTypeObject(0);
|
||||
if (!thisType || thisType->proto->getGlobal() != globalObj)
|
||||
return Compile_InlineAbort;
|
||||
|
||||
/*
|
||||
* Constraints modeling this concat have not been generated by inference,
|
||||
* so check that type information already reflects possible side effects of
|
||||
* this call.
|
||||
*/
|
||||
thisTypes->addFreeze(cx);
|
||||
argTypes->addFreeze(cx);
|
||||
types::TypeSet *thisElemTypes = thisType->getProperty(cx, JSID_VOID, false);
|
||||
if (!thisElemTypes)
|
||||
return Compile_Error;
|
||||
if (!pushedTypeSet(0)->hasType(types::Type::ObjectType(thisType)))
|
||||
return Compile_InlineAbort;
|
||||
for (unsigned i = 0; i < argTypes->getObjectCount(); i++) {
|
||||
if (argTypes->getSingleObject(i))
|
||||
return Compile_InlineAbort;
|
||||
types::TypeObject *argType = argTypes->getTypeObject(i);
|
||||
if (!argType)
|
||||
continue;
|
||||
types::TypeSet *elemTypes = argType->getProperty(cx, JSID_VOID, false);
|
||||
if (!elemTypes)
|
||||
return Compile_Error;
|
||||
if (!elemTypes->knownSubset(cx, thisElemTypes))
|
||||
return Compile_InlineAbort;
|
||||
}
|
||||
|
||||
/* Test for 'length == initializedLength' on both arrays. */
|
||||
|
||||
RegisterID reg = frame.allocReg();
|
||||
Int32Key key = Int32Key::FromRegister(reg);
|
||||
|
||||
RegisterID objReg = frame.tempRegForData(thisValue);
|
||||
masm.load32(Address(objReg, offsetof(JSObject, privateData)), reg);
|
||||
Jump initlenOneGuard = masm.guardArrayExtent(offsetof(JSObject, initializedLength),
|
||||
objReg, key, Assembler::NotEqual);
|
||||
stubcc.linkExit(initlenOneGuard, Uses(3));
|
||||
|
||||
objReg = frame.tempRegForData(argValue);
|
||||
masm.load32(Address(objReg, offsetof(JSObject, privateData)), reg);
|
||||
Jump initlenTwoGuard = masm.guardArrayExtent(offsetof(JSObject, initializedLength),
|
||||
objReg, key, Assembler::NotEqual);
|
||||
stubcc.linkExit(initlenTwoGuard, Uses(3));
|
||||
|
||||
frame.freeReg(reg);
|
||||
frame.syncAndForgetEverything();
|
||||
|
||||
/*
|
||||
* The current stack layout is 'CALLEE THIS ARG'. Allocate the result and
|
||||
* scribble it over the callee, which will be its final position after the
|
||||
* call.
|
||||
*/
|
||||
|
||||
JSObject *templateObject = NewDenseEmptyArray(cx, thisType->proto);
|
||||
if (!templateObject)
|
||||
return Compile_Error;
|
||||
templateObject->setType(thisType);
|
||||
|
||||
RegisterID result = Registers::ReturnReg;
|
||||
Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
|
||||
stubcc.linkExit(emptyFreeList, Uses(3));
|
||||
|
||||
masm.storeValueFromComponents(ImmType(JSVAL_TYPE_OBJECT), result, frame.addressOf(frame.peek(-3)));
|
||||
INLINE_STUBCALL(stubs::ArrayConcatTwoArrays, REJOIN_FALLTHROUGH);
|
||||
|
||||
stubcc.leave();
|
||||
stubcc.masm.move(Imm32(1), Registers::ArgReg1);
|
||||
OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
|
||||
|
||||
frame.popn(3);
|
||||
frame.pushSynced(JSVAL_TYPE_OBJECT);
|
||||
|
||||
stubcc.rejoin(Changes(1));
|
||||
return Compile_Okay;
|
||||
}
|
||||
|
||||
CompileStatus
|
||||
mjit::Compiler::compileArrayWithLength(uint32 argc)
|
||||
{
|
||||
@ -752,6 +840,9 @@ mjit::Compiler::inlineNativeFunction(uint32 argc, bool callingNew)
|
||||
}
|
||||
} else if (argc == 1) {
|
||||
FrameEntry *arg = frame.peek(-1);
|
||||
types::TypeSet *argTypes = frame.extra(arg).types;
|
||||
if (!argTypes)
|
||||
return Compile_InlineAbort;
|
||||
JSValueType argType = arg->isTypeKnown() ? arg->getKnownType() : JSVAL_TYPE_UNKNOWN;
|
||||
|
||||
if (native == js_math_abs) {
|
||||
@ -792,6 +883,12 @@ mjit::Compiler::inlineNativeFunction(uint32 argc, bool callingNew)
|
||||
return compileArrayPush(thisValue, arg);
|
||||
}
|
||||
}
|
||||
if (native == js::array_concat && argType == JSVAL_TYPE_OBJECT &&
|
||||
thisType == JSVAL_TYPE_OBJECT && type == JSVAL_TYPE_OBJECT &&
|
||||
!thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) &&
|
||||
!argTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) {
|
||||
return compileArrayConcat(thisTypes, argTypes, thisValue, arg);
|
||||
}
|
||||
} else if (argc == 2) {
|
||||
FrameEntry *arg1 = frame.peek(-2);
|
||||
FrameEntry *arg2 = frame.peek(-1);
|
||||
|
@ -51,8 +51,8 @@ ThrowException(VMFrame &f)
|
||||
*f.returnAddressLocation() = ptr;
|
||||
}
|
||||
|
||||
#define THROW() do { ThrowException(f); return; } while (0)
|
||||
#define THROWV(v) do { ThrowException(f); return v; } while (0)
|
||||
#define THROW() do { mjit::ThrowException(f); return; } while (0)
|
||||
#define THROWV(v) do { mjit::ThrowException(f); return v; } while (0)
|
||||
|
||||
static inline JSObject *
|
||||
ValueToObject(JSContext *cx, Value *vp)
|
||||
|
@ -235,8 +235,8 @@ void JS_FASTCALL AnyFrameEpilogue(VMFrame &f);
|
||||
JSObject * JS_FASTCALL
|
||||
NewDenseUnallocatedArray(VMFrame &f, uint32 length);
|
||||
|
||||
void JS_FASTCALL
|
||||
ArrayShift(VMFrame &f);
|
||||
void JS_FASTCALL ArrayConcatTwoArrays(VMFrame &f);
|
||||
void JS_FASTCALL ArrayShift(VMFrame &f);
|
||||
|
||||
} /* namespace stubs */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user