Commit a3812102 authored by barraclough@apple.com's avatar barraclough@apple.com

Changed the handling for removing and adding elements at the front

of an array.  The code now keeps a bias that indicates the amount of
JSValue sized holes are prior to the ArrayStorage block.  This means
that shift operations are now memmove's of the header part of
the ArrayStorage and unshift operations are similar, but may require a
realloc first to create the space.  Similar operations are performed
for special cases of splice and slice.
Also optimized the new Array(size) case so that we don't allocate and
initialize array elements until the JS code starts using elements.
The array growth code is slightly more aggressive for initial growth
based on size growth of any previous array.

Patch by Michael Saboff <msaboff@apple.com> on 2010-07-29
Reviewed by Gavin Barraclough.

* Configurations/JavaScriptCore.xcconfig:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_get_by_val):
(JSC::JIT::emit_op_put_by_val):
(JSC::JIT::privateCompilePatchGetArrayLength):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_get_by_val):
(JSC::JIT::emit_op_put_by_val):
(JSC::JIT::privateCompilePatchGetArrayLength):
* runtime/ArrayPrototype.cpp:
(JSC::arrayProtoFuncShift):
(JSC::arrayProtoFuncSplice):
(JSC::arrayProtoFuncUnShift):
* runtime/JSArray.cpp:
(JSC::JSArray::JSArray):
(JSC::JSArray::~JSArray):
(JSC::JSArray::getOwnPropertySlot):
(JSC::JSArray::getOwnPropertyDescriptor):
(JSC::JSArray::put):
(JSC::JSArray::putSlowCase):
(JSC::JSArray::deleteProperty):
(JSC::JSArray::getOwnPropertyNames):
(JSC::JSArray::getNewVectorLength):
(JSC::JSArray::increaseVectorLength):
(JSC::JSArray::increaseVectorPrefixLength):
(JSC::JSArray::setLength):
(JSC::JSArray::pop):
(JSC::JSArray::push):
(JSC::JSArray::shiftCount):
(JSC::JSArray::unshiftCount):
(JSC::JSArray::sortNumeric):
(JSC::JSArray::sort):
(JSC::JSArray::fillArgList):
(JSC::JSArray::copyToRegisters):
(JSC::JSArray::compactForSorting):
(JSC::JSArray::subclassData):
(JSC::JSArray::setSubclassData):
(JSC::JSArray::checkConsistency):
* runtime/JSArray.h:
(JSC::JSArray::length):
(JSC::JSArray::canGetIndex):
(JSC::JSArray::getIndex):
(JSC::JSArray::setIndex):
(JSC::JSArray::uncheckedSetIndex):
(JSC::JSArray::arrayStorage):
(JSC::JSArray::setArrayStorage):
(JSC::JSArray::markChildrenDirect):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@64320 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 923c29ba
2010-07-29 Michael Saboff <msaboff@apple.com>
Reviewed by Gavin Barraclough.
Changed the handling for removing and adding elements at the front
of an array. The code now keeps a bias that indicates the amount of
JSValue sized holes are prior to the ArrayStorage block. This means
that shift operations are now memmove's of the header part of
the ArrayStorage and unshift operations are similar, but may require a
realloc first to create the space. Similar operations are performed
for special cases of splice and slice.
Also optimized the new Array(size) case so that we don't allocate and
initialize array elements until the JS code starts using elements.
The array growth code is slightly more aggressive for initial growth
based on size growth of any previous array.
* Configurations/JavaScriptCore.xcconfig:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_get_by_val):
(JSC::JIT::emit_op_put_by_val):
(JSC::JIT::privateCompilePatchGetArrayLength):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_get_by_val):
(JSC::JIT::emit_op_put_by_val):
(JSC::JIT::privateCompilePatchGetArrayLength):
* runtime/ArrayPrototype.cpp:
(JSC::arrayProtoFuncShift):
(JSC::arrayProtoFuncSplice):
(JSC::arrayProtoFuncUnShift):
* runtime/JSArray.cpp:
(JSC::JSArray::JSArray):
(JSC::JSArray::~JSArray):
(JSC::JSArray::getOwnPropertySlot):
(JSC::JSArray::getOwnPropertyDescriptor):
(JSC::JSArray::put):
(JSC::JSArray::putSlowCase):
(JSC::JSArray::deleteProperty):
(JSC::JSArray::getOwnPropertyNames):
(JSC::JSArray::getNewVectorLength):
(JSC::JSArray::increaseVectorLength):
(JSC::JSArray::increaseVectorPrefixLength):
(JSC::JSArray::setLength):
(JSC::JSArray::pop):
(JSC::JSArray::push):
(JSC::JSArray::shiftCount):
(JSC::JSArray::unshiftCount):
(JSC::JSArray::sortNumeric):
(JSC::JSArray::sort):
(JSC::JSArray::fillArgList):
(JSC::JSArray::copyToRegisters):
(JSC::JSArray::compactForSorting):
(JSC::JSArray::subclassData):
(JSC::JSArray::setSubclassData):
(JSC::JSArray::checkConsistency):
* runtime/JSArray.h:
(JSC::JSArray::length):
(JSC::JSArray::canGetIndex):
(JSC::JSArray::getIndex):
(JSC::JSArray::setIndex):
(JSC::JSArray::uncheckedSetIndex):
(JSC::JSArray::arrayStorage):
(JSC::JSArray::setArrayStorage):
(JSC::JSArray::markChildrenDirect):
2010-07-29 Michael Saboff <msaboff@apple.com>
Reviewed by Darin Adler.
......
......@@ -103,10 +103,10 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction)
emitJumpSlowCaseIfNotJSCell(regT0, base);
addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr)));
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT2);
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_vector)), regT2);
addSlowCase(branch32(AboveOrEqual, regT1, Address(regT0, OBJECT_OFFSETOF(JSArray, m_vectorLength))));
loadPtr(BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), regT0);
loadPtr(BaseIndex(regT2, regT1, ScalePtr), regT0);
addSlowCase(branchTestPtr(Zero, regT0));
emitPutVirtualRegister(dst);
......@@ -214,22 +214,21 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction)
addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr)));
addSlowCase(branch32(AboveOrEqual, regT1, Address(regT0, OBJECT_OFFSETOF(JSArray, m_vectorLength))));
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT2);
Jump empty = branchTestPtr(Zero, BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_vector)), regT2);
Jump empty = branchTestPtr(Zero, BaseIndex(regT2, regT1, ScalePtr));
Label storeResult(this);
emitGetVirtualRegister(value, regT0);
storePtr(regT0, BaseIndex(regT2, regT1, ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
storePtr(regT0, BaseIndex(regT2, regT1, ScalePtr));
Jump end = jump();
empty.link(this);
add32(Imm32(1), Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector)));
branch32(Below, regT1, Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length))).linkTo(storeResult, this);
add32(Imm32(1), Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector)-OBJECT_OFFSETOF(ArrayStorage, m_vector)));
branch32(Below, regT1, Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)-OBJECT_OFFSETOF(ArrayStorage, m_vector))).linkTo(storeResult, this);
move(regT1, regT0);
add32(Imm32(1), regT0);
store32(regT0, Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)));
store32(regT0, Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)-OBJECT_OFFSETOF(ArrayStorage, m_vector)));
jump().linkTo(storeResult, this);
end.link(this);
......@@ -726,9 +725,8 @@ void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress)
Jump failureCases1 = branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr));
// Checks out okay! - get the length from the storage
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT2);
load32(Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)), regT2);
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_vector)), regT3);
load32(Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_length)-OBJECT_OFFSETOF(ArrayStorage, m_vector)), regT2);
Jump failureCases2 = branch32(Above, regT2, Imm32(JSImmediate::maxImmediateInt));
emitFastArithIntToImmNoCheck(regT2, regT0);
......
......@@ -311,11 +311,11 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction)
emitJumpSlowCaseIfNotJSCell(base, regT1);
addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr)));
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT3);
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_vector)), regT3);
addSlowCase(branch32(AboveOrEqual, regT2, Address(regT0, OBJECT_OFFSETOF(JSArray, m_vectorLength))));
load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT1); // tag
load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT0); // payload
load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT1); // tag
load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT0); // payload
addSlowCase(branch32(Equal, regT1, Imm32(JSValue::EmptyValueTag)));
emitStore(dst, regT1, regT0);
......@@ -364,22 +364,22 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction)
addSlowCase(branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr)));
addSlowCase(branch32(AboveOrEqual, regT2, Address(regT0, OBJECT_OFFSETOF(JSArray, m_vectorLength))));
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT3);
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_vector)), regT3);
Jump empty = branch32(Equal, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), Imm32(JSValue::EmptyValueTag));
Jump empty = branch32(Equal, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), Imm32(JSValue::EmptyValueTag));
Label storeResult(this);
emitLoad(value, regT1, regT0);
store32(regT0, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); // payload
store32(regT1, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); // tag
store32(regT0, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); // payload
store32(regT1, BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); // tag
Jump end = jump();
empty.link(this);
add32(Imm32(1), Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector)));
branch32(Below, regT2, Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_length))).linkTo(storeResult, this);
add32(Imm32(1), Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector)-OBJECT_OFFSETOF(ArrayStorage, m_vector)));
branch32(Below, regT2, Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_length)-OBJECT_OFFSETOF(ArrayStorage, m_vector))).linkTo(storeResult, this);
add32(Imm32(1), regT2, regT0);
store32(regT0, Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_length)));
store32(regT0, Address(regT3, OBJECT_OFFSETOF(ArrayStorage, m_length)-OBJECT_OFFSETOF(ArrayStorage, m_vector)));
jump().linkTo(storeResult, this);
end.link(this);
......@@ -731,8 +731,8 @@ void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress)
Jump failureCases1 = branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsArrayVPtr));
// Checks out okay! - get the length from the storage
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_storage)), regT2);
load32(Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)), regT2);
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSArray, m_vector)), regT2);
load32(Address(regT2, OBJECT_OFFSETOF(ArrayStorage, m_length)-OBJECT_OFFSETOF(ArrayStorage, m_vector)), regT2);
Jump failureCases2 = branch32(Above, regT2, Imm32(INT_MAX));
move(regT2, regT0);
......
......@@ -424,13 +424,17 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncShift(ExecState* exec)
result = jsUndefined();
} else {
result = thisObj->get(exec, 0);
for (unsigned k = 1; k < length; k++) {
if (JSValue obj = getProperty(exec, thisObj, k))
thisObj->put(exec, k - 1, obj);
else
thisObj->deleteProperty(exec, k - 1);
if (isJSArray(&exec->globalData(), thisObj))
((JSArray *)thisObj)->shiftCount(exec, 1);
else {
for (unsigned k = 1; k < length; k++) {
if (JSValue obj = getProperty(exec, thisObj, k))
thisObj->put(exec, k - 1, obj);
else
thisObj->deleteProperty(exec, k - 1);
}
thisObj->deleteProperty(exec, length - 1);
}
thisObj->deleteProperty(exec, length - 1);
putProperty(exec, thisObj, exec->propertyNames().length, jsNumber(exec, length - 1));
}
return JSValue::encode(result);
......@@ -578,20 +582,28 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
unsigned additionalArgs = std::max<int>(exec->argumentCount() - 2, 0);
if (additionalArgs != deleteCount) {
if (additionalArgs < deleteCount) {
for (unsigned k = begin; k < length - deleteCount; ++k) {
if (JSValue v = getProperty(exec, thisObj, k + deleteCount))
thisObj->put(exec, k + additionalArgs, v);
else
thisObj->deleteProperty(exec, k + additionalArgs);
if ((!begin) && (isJSArray(&exec->globalData(), thisObj)))
((JSArray *)thisObj)->shiftCount(exec, deleteCount - additionalArgs);
else {
for (unsigned k = begin; k < length - deleteCount; ++k) {
if (JSValue v = getProperty(exec, thisObj, k + deleteCount))
thisObj->put(exec, k + additionalArgs, v);
else
thisObj->deleteProperty(exec, k + additionalArgs);
}
for (unsigned k = length; k > length - deleteCount + additionalArgs; --k)
thisObj->deleteProperty(exec, k - 1);
}
for (unsigned k = length; k > length - deleteCount + additionalArgs; --k)
thisObj->deleteProperty(exec, k - 1);
} else {
for (unsigned k = length - deleteCount; k > begin; --k) {
if (JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1))
thisObj->put(exec, k + additionalArgs - 1, obj);
else
thisObj->deleteProperty(exec, k + additionalArgs - 1);
if ((!begin) && (isJSArray(&exec->globalData(), thisObj)))
((JSArray *)thisObj)->unshiftCount(exec, additionalArgs - deleteCount);
else {
for (unsigned k = length - deleteCount; k > begin; --k) {
if (JSValue obj = getProperty(exec, thisObj, k + deleteCount - 1))
thisObj->put(exec, k + additionalArgs - 1, obj);
else
thisObj->deleteProperty(exec, k + additionalArgs - 1);
}
}
}
}
......@@ -610,12 +622,16 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
// 15.4.4.13
unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
unsigned nrArgs = exec->argumentCount();
if (nrArgs) {
for (unsigned k = length; k > 0; --k) {
if (JSValue v = getProperty(exec, thisObj, k - 1))
thisObj->put(exec, k + nrArgs - 1, v);
else
thisObj->deleteProperty(exec, k + nrArgs - 1);
if ((nrArgs) && (length)) {
if (isJSArray(&exec->globalData(), thisObj))
((JSArray *)thisObj)->unshiftCount(exec, nrArgs);
else {
for (unsigned k = length; k > 0; --k) {
if (JSValue v = getProperty(exec, thisObj, k - 1))
thisObj->put(exec, k + nrArgs - 1, v);
else
thisObj->deleteProperty(exec, k + nrArgs - 1);
}
}
}
for (unsigned k = 0; k < nrArgs; ++k)
......
This diff is collapsed.
......@@ -29,8 +29,13 @@ namespace JSC {
typedef HashMap<unsigned, JSValue> SparseArrayValueMap;
// This struct holds the actual data values of an array. A JSArray object points to it's contained ArrayStorage
// struct by pointing to m_vector. To access the contained ArrayStorage struct, use the getStorage() and
// setStorage() methods. It is important to note that there may be space before the ArrayStorage that
// is used to quick unshift / shift operation. The actual allocated pointer is available by using:
// getStorage() - m_indexBias * sizeof(JSValue)
struct ArrayStorage {
unsigned m_length;
unsigned m_length; // The "length" property on the array
unsigned m_numValuesInVector;
SparseArrayValueMap* m_sparseValueMap;
void* subclassData; // A JSArray subclass can use this to fill the vector lazily.
......@@ -67,8 +72,8 @@ namespace JSC {
virtual void put(ExecState*, unsigned propertyName, JSValue); // FIXME: Make protected and add setItem.
static JS_EXPORTDATA const ClassInfo info;
unsigned length() const { return m_storage->m_length; }
unsigned length() const { return arrayStorage()->m_length; }
void setLength(unsigned); // OK to use on new arrays, but not if it might be a RegExpMatchArray.
void sort(ExecState*);
......@@ -78,33 +83,39 @@ namespace JSC {
void push(ExecState*, JSValue);
JSValue pop();
bool canGetIndex(unsigned i) { return i < m_vectorLength && m_storage->m_vector[i]; }
void shiftCount(ExecState*, int count);
void unshiftCount(ExecState*, int count);
bool canGetIndex(unsigned i) { return i < m_vectorLength && m_vector[i]; }
JSValue getIndex(unsigned i)
{
ASSERT(canGetIndex(i));
return m_storage->m_vector[i];
return m_vector[i];
}
bool canSetIndex(unsigned i) { return i < m_vectorLength; }
void setIndex(unsigned i, JSValue v)
{
ASSERT(canSetIndex(i));
JSValue& x = m_storage->m_vector[i];
JSValue& x = m_vector[i];
if (!x) {
++m_storage->m_numValuesInVector;
if (i >= m_storage->m_length)
m_storage->m_length = i + 1;
ArrayStorage *storage = arrayStorage();
++storage->m_numValuesInVector;
if (i >= storage->m_length)
storage->m_length = i + 1;
}
x = v;
}
void uncheckedSetIndex(unsigned i, JSValue v)
{
ASSERT(canSetIndex(i));
ArrayStorage *storage = arrayStorage();
#if CHECK_ARRAY_CONSISTENCY
ASSERT(m_storage->m_inCompactInitialization);
ASSERT(storage->m_inCompactInitialization);
#endif
m_storage->m_vector[i] = v;
storage->m_vector[i] = v;
}
void fillArgList(ExecState*, MarkedArgumentBuffer&);
......@@ -127,6 +138,16 @@ namespace JSC {
void* subclassData() const;
void setSubclassData(void*);
inline ArrayStorage *arrayStorage() const
{
return reinterpret_cast<ArrayStorage*>(reinterpret_cast<char*>(m_vector) - (sizeof(ArrayStorage) - sizeof(JSValue)));
}
inline void setArrayStorage(ArrayStorage *storage)
{
m_vector = &storage->m_vector[0];
}
private:
virtual const ClassInfo* classInfo() const { return &info; }
......@@ -134,15 +155,18 @@ namespace JSC {
bool getOwnPropertySlotSlowCase(ExecState*, unsigned propertyName, PropertySlot&);
void putSlowCase(ExecState*, unsigned propertyName, JSValue);
unsigned getNewVectorLength(unsigned desiredLength);
bool increaseVectorLength(unsigned newLength);
bool increaseVectorPrefixLength(unsigned newLength);
unsigned compactForSorting();
enum ConsistencyCheckType { NormalConsistencyCheck, DestructorConsistencyCheck, SortConsistencyCheck };
void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck);
unsigned m_vectorLength;
ArrayStorage* m_storage;
unsigned m_vectorLength; // The valid length of m_vector
int m_indexBias; // The number of JSValue sized blocks before ArrayStorage.
JSValue* m_vector; // Copy of ArrayStorage.m_vector. Used for quick vector access and to materialize ArrayStorage ptr.
};
JSArray* asArray(JSValue);
......@@ -168,7 +192,7 @@ namespace JSC {
{
JSObject::markChildrenDirect(markStack);
ArrayStorage* storage = m_storage;
ArrayStorage* storage = arrayStorage();
unsigned usedVectorLength = std::min(storage->m_length, m_vectorLength);
markStack.appendValues(storage->m_vector, usedVectorLength, MayContainNullValues);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment