JM: OOL fast path for setting array holes, bug 580355

This commit is contained in:
Brian Hackett 2010-07-28 13:07:26 -07:00
parent 211b338724
commit e56731d546
5 changed files with 86 additions and 4 deletions

View File

@ -811,7 +811,8 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
if (!obj->isDenseArray())
return js_SetProperty(cx, obj, id, vp);
if (!js_IdIsIndex(id, &i) || INDEX_TOO_SPARSE(obj, i)) {
if (!js_IdIsIndex(id, &i) || js_PrototypeHasIndexedProperties(cx, obj) ||
INDEX_TOO_SPARSE(obj, i)) {
if (!obj->makeDenseArraySlow(cx))
return JS_FALSE;
return js_SetProperty(cx, obj, id, vp);

View File

@ -222,9 +222,7 @@ struct JSScope : public JSObjectMap
#endif
JSObject *object; /* object that owns this scope */
uint32 freeslot; /* index of next free slot in object */
protected:
uint8 flags; /* flags, see below */
public:
int8 hashShift; /* multiplicative hash shift */
uint16 spare; /* reserved */

View File

@ -40,6 +40,7 @@
#include "jsbool.h"
#include "jslibmath.h"
#include "jsnum.h"
#include "jsscope.h"
#include "methodjit/MethodJIT.h"
#include "methodjit/Compiler.h"
#include "methodjit/StubCalls.h"
@ -964,6 +965,14 @@ mjit::Compiler::jsop_setelem()
} else {
RegisterID idReg = maybeIdReg.reg();
/*
* Register for use only in OOL hole path. TODO: would be nice
* for the frame to do any associated spilling in the OOL path.
*/
RegisterID T1 = frame.allocReg();
Label syncTarget = stubcc.syncExitAndJump(Uses(3));
/* guard not a hole */
BaseIndex slot(objReg, idReg, Assembler::JSVAL_SCALE);
#if defined JS_NUNBOX32
@ -972,7 +981,57 @@ mjit::Compiler::jsop_setelem()
masm.loadTypeTag(slot, Registers::ValueReg);
Jump notHole = masm.branchPtr(Assembler::Equal, Registers::ValueReg, ImmType(JSVAL_TYPE_MAGIC));
#endif
stubcc.linkExit(notHole, Uses(3));
/* Make an OOL path for setting array holes. */
Label lblHole = stubcc.masm.label();
stubcc.linkExitDirect(notHole, lblHole);
/* Need a new handle on the object, as objReg now holds the dslots. */
RegisterID baseReg = frame.tempRegForData(obj, objReg, stubcc.masm);
/*
* Check if the object has a prototype with indexed properties,
* in which case it might have a setter for this element. For dense
* arrays we only need to check Array.prototype and Object.prototype.
*/
/*
* Test for indexed properties in Array.prototype. flags is a one byte
* quantity, but will be aligned on 4 bytes.
*/
stubcc.masm.loadPtr(Address(baseReg, offsetof(JSObject, proto)), T1);
stubcc.masm.loadPtr(Address(T1, offsetof(JSObject, map)), T1);
stubcc.masm.load32(Address(T1, offsetof(JSScope, flags)), T1);
stubcc.masm.and32(Imm32(JSScope::INDEXED_PROPERTIES), T1);
Jump extendedArray = stubcc.masm.branchTest32(Assembler::NonZero, T1, T1);
extendedArray.linkTo(syncTarget, &stubcc.masm);
/* Test for indexed properties in Object.prototype. */
stubcc.masm.loadPtr(Address(baseReg, offsetof(JSObject, proto)), T1);
stubcc.masm.loadPtr(Address(T1, offsetof(JSObject, proto)), T1);
stubcc.masm.loadPtr(Address(T1, offsetof(JSObject, map)), T1);
stubcc.masm.load32(Address(T1, offsetof(JSScope, flags)), T1);
stubcc.masm.and32(Imm32(JSScope::INDEXED_PROPERTIES), T1);
Jump extendedObject = stubcc.masm.branchTest32(Assembler::NonZero, T1, T1);
extendedObject.linkTo(syncTarget, &stubcc.masm);
/* Update the array length if needed. Don't worry about overflow. */
Address arrayLength(baseReg, offsetof(JSObject, fslots[JSObject::JSSLOT_ARRAY_LENGTH]));
stubcc.masm.loadPayload(arrayLength, T1);
Jump underLength = stubcc.masm.branch32(Assembler::LessThan, idReg, T1);
stubcc.masm.move(idReg, T1);
stubcc.masm.add32(Imm32(1), T1);
stubcc.masm.storePayload(T1, arrayLength);
underLength.linkTo(stubcc.masm.label(), &stubcc.masm);
/* Restore the dslots register if we clobbered it with the object. */
if (baseReg == objReg)
stubcc.masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
/* Rejoin OOL path with inline path to do the store itself. */
Jump jmpHoleExit = stubcc.masm.jump();
Label lblRejoin = masm.label();
stubcc.crossJump(jmpHoleExit, lblRejoin);
stubcc.leave();
stubcc.call(stubs::SetElem);
@ -993,6 +1052,7 @@ mjit::Compiler::jsop_setelem()
}
frame.freeReg(idReg);
frame.freeReg(T1);
}
frame.freeReg(objReg);

View File

@ -442,6 +442,23 @@ FrameState::tempRegInMaskForData(FrameEntry *fe, uint32 mask)
return reg;
}
inline JSC::MacroAssembler::RegisterID
FrameState::tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const
{
JS_ASSERT(!fe->data.isConstant());
if (fe->isCopy())
fe = fe->copyOf();
if (fe->data.inRegister()) {
JS_ASSERT(fe->data.reg() != reg);
return fe->data.reg();
} else {
masm.loadPayload(addressOf(fe), reg);
return reg;
}
}
inline bool
FrameState::shouldAvoidTypeRemat(FrameEntry *fe)
{

View File

@ -320,6 +320,12 @@ class FrameState
*/
inline RegisterID tempRegInMaskForData(FrameEntry *fe, uint32 mask);
/*
* Same as above, except loads into reg (using masm) if the entry does not
* already have a register, and does not change the frame state in doing so.
*/
inline RegisterID tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const;
/*
* Forcibly loads the type tag for the specified FrameEntry
* into a register already marked as owning the type.