Reviewed by Gavin Barraclough.
Original patch by John McCall. Updated by Cameron Zwarich. Further refined by me.
- Assorted speedups to property access
~.3%-1% speedup on SunSpider
1) When we know from the structure ID that an object is using inline storage, plant direct
loads and stores against it; no need to indirect through storage pointer.
2) Also because of the above, union the property storage pointer with the first inline property
slot and add an extra inline property slot.
* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::CodeLocationInstruction::CodeLocationInstruction):
(JSC::AbstractMacroAssembler::CodeLocationInstruction::patchLoadToLEA):
(JSC::::CodeLocationCommon::instructionAtOffset):
* assembler/MacroAssembler.h:
(JSC::MacroAssembler::storePtr):
* assembler/MacroAssemblerX86.h:
(JSC::MacroAssemblerX86::store32):
* assembler/MacroAssemblerX86_64.h:
(JSC::MacroAssemblerX86_64::storePtr):
* assembler/X86Assembler.h:
(JSC::X86Assembler::movq_EAXm):
(JSC::X86Assembler::movl_rm):
(JSC::X86Assembler::patchLoadToLEA):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::compileGetByIdHotPath):
(JSC::JIT::compilePutByIdHotPath):
(JSC::JIT::compilePutDirectOffset):
(JSC::JIT::compileGetDirectOffset):
(JSC::JIT::privateCompilePutByIdTransition):
(JSC::JIT::patchGetByIdSelf):
(JSC::JIT::patchPutByIdReplace):
(JSC::JIT::privateCompileGetByIdSelf):
(JSC::JIT::privateCompileGetByIdProto):
(JSC::JIT::privateCompileGetByIdSelfList):
(JSC::JIT::privateCompileGetByIdProtoList):
(JSC::JIT::privateCompileGetByIdChainList):
(JSC::JIT::privateCompileGetByIdChain):
(JSC::JIT::privateCompilePutByIdReplace):
* runtime/JSObject.cpp:
(JSC::JSObject::mark):
(JSC::JSObject::removeDirect):
* runtime/JSObject.h:
(JSC::JSObject::propertyStorage):
(JSC::JSObject::getDirect):
(JSC::JSObject::getOffset):
(JSC::JSObject::offsetForLocation):
(JSC::JSObject::locationForOffset):
(JSC::JSObject::getDirectOffset):
(JSC::JSObject::putDirectOffset):
(JSC::JSObject::isUsingInlineStorage):
(JSC::JSObject::):
(JSC::JSObject::JSObject):
(JSC::JSObject::~JSObject):
(JSC::Structure::isUsingInlineStorage):
(JSC::JSObject::putDirect):
(JSC::JSObject::putDirectWithoutTransition):
(JSC::JSObject::allocatePropertyStorageInline):
* runtime/Structure.h:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@43432
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2009-05-09 Maciej Stachowiak <mjs@apple.com>
+
+ Reviewed by Gavin Barraclough.
+
+ Original patch by John McCall. Updated by Cameron Zwarich. Further refined by me.
+
+ - Assorted speedups to property access
+
+ ~.3%-1% speedup on SunSpider
+
+ 1) When we know from the structure ID that an object is using inline storage, plant direct
+ loads and stores against it; no need to indirect through storage pointer.
+
+ 2) Also because of the above, union the property storage pointer with the first inline property
+ slot and add an extra inline property slot.
+
+ * assembler/AbstractMacroAssembler.h:
+ (JSC::AbstractMacroAssembler::CodeLocationInstruction::CodeLocationInstruction):
+ (JSC::AbstractMacroAssembler::CodeLocationInstruction::patchLoadToLEA):
+ (JSC::::CodeLocationCommon::instructionAtOffset):
+ * assembler/MacroAssembler.h:
+ (JSC::MacroAssembler::storePtr):
+ * assembler/MacroAssemblerX86.h:
+ (JSC::MacroAssemblerX86::store32):
+ * assembler/MacroAssemblerX86_64.h:
+ (JSC::MacroAssemblerX86_64::storePtr):
+ * assembler/X86Assembler.h:
+ (JSC::X86Assembler::movq_EAXm):
+ (JSC::X86Assembler::movl_rm):
+ (JSC::X86Assembler::patchLoadToLEA):
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ * jit/JIT.h:
+ * jit/JITPropertyAccess.cpp:
+ (JSC::JIT::compileGetByIdHotPath):
+ (JSC::JIT::compilePutByIdHotPath):
+ (JSC::JIT::compilePutDirectOffset):
+ (JSC::JIT::compileGetDirectOffset):
+ (JSC::JIT::privateCompilePutByIdTransition):
+ (JSC::JIT::patchGetByIdSelf):
+ (JSC::JIT::patchPutByIdReplace):
+ (JSC::JIT::privateCompileGetByIdSelf):
+ (JSC::JIT::privateCompileGetByIdProto):
+ (JSC::JIT::privateCompileGetByIdSelfList):
+ (JSC::JIT::privateCompileGetByIdProtoList):
+ (JSC::JIT::privateCompileGetByIdChainList):
+ (JSC::JIT::privateCompileGetByIdChain):
+ (JSC::JIT::privateCompilePutByIdReplace):
+ * runtime/JSObject.cpp:
+ (JSC::JSObject::mark):
+ (JSC::JSObject::removeDirect):
+ * runtime/JSObject.h:
+ (JSC::JSObject::propertyStorage):
+ (JSC::JSObject::getDirect):
+ (JSC::JSObject::getOffset):
+ (JSC::JSObject::offsetForLocation):
+ (JSC::JSObject::locationForOffset):
+ (JSC::JSObject::getDirectOffset):
+ (JSC::JSObject::putDirectOffset):
+ (JSC::JSObject::isUsingInlineStorage):
+ (JSC::JSObject::):
+ (JSC::JSObject::JSObject):
+ (JSC::JSObject::~JSObject):
+ (JSC::Structure::isUsingInlineStorage):
+ (JSC::JSObject::putDirect):
+ (JSC::JSObject::putDirectWithoutTransition):
+ (JSC::JSObject::allocatePropertyStorageInline):
+ * runtime/Structure.h:
+
2009-05-09 Geoffrey Garen <ggaren@apple.com>
Reviewed by Gavin Barraclough.
public:
class Jump;
class PatchBuffer;
+ class CodeLocationInstruction;
class CodeLocationLabel;
class CodeLocationJump;
class CodeLocationCall;
// and the labels will always be a fixed distance apart, these
// methods may be used to recover a handle that has nopw been
// retained, based on a known fixed relative offset from one that has.
+ CodeLocationInstruction instructionAtOffset(int offset);
CodeLocationLabel labelAtOffset(int offset);
CodeLocationJump jumpAtOffset(int offset);
CodeLocationCall callAtOffset(int offset);
void* m_location;
};
+ // CodeLocationInstruction:
+ //
+ // An arbitrary instruction in the JIT code.
+ class CodeLocationInstruction : public CodeLocationCommon {
+ friend class CodeLocationCommon;
+ public:
+ CodeLocationInstruction()
+ {
+ }
+
+ void patchLoadToLEA() {
+ AssemblerType::patchLoadToLEA(reinterpret_cast<intptr_t>(this->m_location));
+ }
+
+ private:
+ explicit CodeLocationInstruction(void* location)
+ : CodeLocationCommon(location)
+ {
+ }
+ };
+
// CodeLocationLabel:
//
// A point in the JIT code maked with a label.
};
+template <class AssemblerType>
+typename AbstractMacroAssembler<AssemblerType>::CodeLocationInstruction AbstractMacroAssembler<AssemblerType>::CodeLocationCommon::instructionAtOffset(int offset)
+{
+ return typename AbstractMacroAssembler::CodeLocationInstruction(reinterpret_cast<char*>(m_location) + offset);
+}
+
template <class AssemblerType>
typename AbstractMacroAssembler<AssemblerType>::CodeLocationLabel AbstractMacroAssembler<AssemblerType>::CodeLocationCommon::labelAtOffset(int offset)
{
store32(src, address);
}
+ void storePtr(RegisterID src, void* address)
+ {
+ store32(src, address);
+ }
+
void storePtr(ImmPtr imm, ImplicitAddress address)
{
store32(Imm32(imm), address);
m_assembler.movl_i32m(imm.m_value, address);
}
+ void store32(RegisterID src, void* address)
+ {
+ m_assembler.movl_rm(src, address);
+ }
+
Jump branch32(Condition cond, AbsoluteAddress left, RegisterID right)
{
m_assembler.cmpl_rm(right, left.m_ptr);
{
m_assembler.movq_rm(src, address.offset, address.base, address.index, address.scale);
}
+
+ void storePtr(RegisterID src, void* address)
+ {
+ if (src == X86::eax)
+ m_assembler.movq_EAXm(address);
+ else {
+ swap(X86::eax, src);
+ m_assembler.movq_EAXm(address);
+ swap(X86::eax, src);
+ }
+ }
void storePtr(ImmPtr imm, ImplicitAddress address)
{
m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
}
+ void movq_EAXm(void* addr)
+ {
+ m_formatter.oneByteOp64(OP_MOV_OvEAX);
+ m_formatter.immediate64(reinterpret_cast<int64_t>(addr));
+ }
+
void movq_mr(int offset, RegisterID base, RegisterID dst)
{
m_formatter.oneByteOp64(OP_MOV_GvEv, dst, base, offset);
#else
+ void movl_rm(RegisterID src, void* addr)
+ {
+ if (src == X86::eax)
+ movl_EAXm(addr);
+ else
+ m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
+ }
+
void movl_mr(void* addr, RegisterID dst)
{
if (dst == X86::eax)
ASSERT(linkOffset == static_cast<int>(linkOffset));
reinterpret_cast<int*>(reinterpret_cast<ptrdiff_t>(code) + from.m_offset)[-1] = linkOffset;
}
+
+ static void patchLoadToLEA(intptr_t where)
+ {
+ char* ptr = reinterpret_cast<char*>(where);
+ ptr[0] = OP_LEA;
+ }
static void patchJump(intptr_t where, void* destination)
{
Jump noMatch = branchPtr(NotEqual, regT1, Address(regT0, FIELD_OFFSET(JSCell, m_structure))); // Structures don't match
// Load cached property
- loadPtr(Address(regT0, FIELD_OFFSET(JSGlobalObject, m_propertyStorage)), regT0);
+ // Assume that the global object always uses external storage.
+ loadPtr(Address(regT0, FIELD_OFFSET(JSGlobalObject, m_externalStorage)), regT0);
load32(offsetAddr, regT1);
loadPtr(BaseIndex(regT0, regT1, ScalePtr), regT0);
emitPutVirtualRegister(currentInstruction[1].u.operand);
#if PLATFORM(X86_64)
// These architecture specific value are used to enable patching - see comment on op_put_by_id.
static const int patchOffsetPutByIdStructure = 10;
+ static const int patchOffsetPutByIdExternalLoad = 20;
+ static const int patchLengthPutByIdExternalLoad = 4;
static const int patchOffsetPutByIdPropertyMapOffset = 31;
// These architecture specific value are used to enable patching - see comment on op_get_by_id.
static const int patchOffsetGetByIdStructure = 10;
static const int patchOffsetGetByIdBranchToSlowCase = 20;
+ static const int patchOffsetGetByIdExternalLoad = 20;
+ static const int patchLengthGetByIdExternalLoad = 4;
static const int patchOffsetGetByIdPropertyMapOffset = 31;
static const int patchOffsetGetByIdPutResult = 31;
#if ENABLE(OPCODE_SAMPLING)
#else
// These architecture specific value are used to enable patching - see comment on op_put_by_id.
static const int patchOffsetPutByIdStructure = 7;
+ static const int patchOffsetPutByIdExternalLoad = 13;
+ static const int patchLengthPutByIdExternalLoad = 3;
static const int patchOffsetPutByIdPropertyMapOffset = 22;
// These architecture specific value are used to enable patching - see comment on op_get_by_id.
static const int patchOffsetGetByIdStructure = 7;
static const int patchOffsetGetByIdBranchToSlowCase = 13;
+ static const int patchOffsetGetByIdExternalLoad = 13;
+ static const int patchLengthGetByIdExternalLoad = 3;
static const int patchOffsetGetByIdPropertyMapOffset = 22;
static const int patchOffsetGetByIdPutResult = 22;
#if ENABLE(OPCODE_SAMPLING)
enum CompileOpStrictEqType { OpStrictEq, OpNStrictEq };
void compileOpStrictEq(Instruction* instruction, CompileOpStrictEqType type);
+ void compileGetDirectOffset(RegisterID base, RegisterID result, Structure* structure, size_t cachedOffset);
+ void compileGetDirectOffset(JSObject* base, RegisterID temp, RegisterID result, size_t cachedOffset);
+ void compilePutDirectOffset(RegisterID base, RegisterID value, Structure* structure, size_t cachedOffset);
+
void compileFastArith_op_add(Instruction*);
void compileFastArith_op_sub(Instruction*);
void compileFastArith_op_mul(Instruction*);
ASSERT(differenceBetween(hotPathBegin, structureToCompare) == patchOffsetGetByIdStructure);
ASSERT(differenceBetween(hotPathBegin, structureCheck) == patchOffsetGetByIdBranchToSlowCase);
- loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_propertyStorage)), regT0);
+ Label externalLoad(this);
+ loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_externalStorage)), regT0);
+ Label externalLoadComplete(this);
+ ASSERT(differenceBetween(hotPathBegin, externalLoad) == patchOffsetGetByIdExternalLoad);
+ ASSERT(differenceBetween(externalLoad, externalLoadComplete) == patchLengthGetByIdExternalLoad);
+
DataLabel32 displacementLabel = loadPtrWithAddressOffsetPatch(Address(regT0, patchGetByIdDefaultOffset), regT0);
ASSERT(differenceBetween(hotPathBegin, displacementLabel) == patchOffsetGetByIdPropertyMapOffset);
ASSERT(differenceBetween(hotPathBegin, structureToCompare) == patchOffsetPutByIdStructure);
// Plant a load from a bogus ofset in the object's property map; we will patch this later, if it is to be used.
- loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_propertyStorage)), regT0);
+ Label externalLoad(this);
+ loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_externalStorage)), regT0);
+ Label externalLoadComplete(this);
+ ASSERT(differenceBetween(hotPathBegin, externalLoad) == patchOffsetPutByIdExternalLoad);
+ ASSERT(differenceBetween(externalLoad, externalLoadComplete) == patchLengthPutByIdExternalLoad);
+
DataLabel32 displacementLabel = storePtrWithAddressOffsetPatch(regT1, Address(regT0, patchGetByIdDefaultOffset));
ASSERT(differenceBetween(hotPathBegin, displacementLabel) == patchOffsetPutByIdPropertyMapOffset);
}
m_propertyAccessCompilationInfo[propertyAccessInstructionIndex].callReturnLocation = call;
}
+// Compile a store into an object's property storage. May overwrite the
+// value in objectReg.
+void JIT::compilePutDirectOffset(RegisterID base, RegisterID value, Structure* structure, size_t cachedOffset)
+{
+ int offset = cachedOffset * sizeof(JSValue);
+ if (structure->isUsingInlineStorage())
+ offset += FIELD_OFFSET(JSObject, m_inlineStorage);
+ else
+ loadPtr(Address(base, FIELD_OFFSET(JSObject, m_externalStorage)), base);
+ storePtr(value, Address(base, offset));
+}
+
+// Compile a load from an object's property storage. May overwrite base.
+void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, Structure* structure, size_t cachedOffset)
+{
+ int offset = cachedOffset * sizeof(JSValue);
+ if (structure->isUsingInlineStorage())
+ offset += FIELD_OFFSET(JSObject, m_inlineStorage);
+ else
+ loadPtr(Address(base, FIELD_OFFSET(JSObject, m_externalStorage)), base);
+ loadPtr(Address(base, offset), result);
+}
+
+void JIT::compileGetDirectOffset(JSObject* base, RegisterID temp, RegisterID result, size_t cachedOffset)
+{
+ if (base->isUsingInlineStorage())
+ loadPtr(static_cast<void*>(&base->m_inlineStorage[cachedOffset]), result);
+ else {
+ PropertyStorage* protoPropertyStorage = &base->m_externalStorage;
+ loadPtr(static_cast<void*>(protoPropertyStorage), temp);
+ loadPtr(Address(temp, cachedOffset * sizeof(JSValue)), result);
+ }
+}
+
static JSObject* resizePropertyStorage(JSObject* baseObject, int32_t oldSize, int32_t newSize)
{
baseObject->allocatePropertyStorage(oldSize, newSize);
storePtr(ImmPtr(newStructure), Address(regT0, FIELD_OFFSET(JSCell, m_structure)));
// write the value
- loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_propertyStorage)), regT0);
- storePtr(regT1, Address(regT0, cachedOffset * sizeof(JSValue)));
+ compilePutDirectOffset(regT0, regT1, newStructure, cachedOffset);
ret();
// Should probably go to JITStubs::cti_op_get_by_id_fail, but that doesn't do anything interesting right now.
returnAddress.relinkCallerToFunction(JITStubs::cti_op_get_by_id_self_fail);
+ int offset = sizeof(JSValue) * cachedOffset;
+
+ // If we're patching to use inline storage, convert the initial load to a lea; this avoids the extra load
+ // and makes the subsequent load's offset automatically correct
+ if (structure->isUsingInlineStorage())
+ stubInfo->hotPathBegin.instructionAtOffset(patchOffsetGetByIdExternalLoad).patchLoadToLEA();
+
// Patch the offset into the propoerty map to load from, then patch the Structure to look for.
stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetGetByIdStructure).repatch(structure);
- stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetGetByIdPropertyMapOffset).repatch(cachedOffset * sizeof(JSValue));
+ stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetGetByIdPropertyMapOffset).repatch(offset);
}
void JIT::patchPutByIdReplace(StructureStubInfo* stubInfo, Structure* structure, size_t cachedOffset, ProcessorReturnAddress returnAddress)
// Should probably go to JITStubs::cti_op_put_by_id_fail, but that doesn't do anything interesting right now.
returnAddress.relinkCallerToFunction(JITStubs::cti_op_put_by_id_generic);
+ int offset = sizeof(JSValue) * cachedOffset;
+
+ // If we're patching to use inline storage, convert the initial load to a lea; this avoids the extra load
+ // and makes the subsequent load's offset automatically correct
+ if (structure->isUsingInlineStorage())
+ stubInfo->hotPathBegin.instructionAtOffset(patchOffsetGetByIdExternalLoad).patchLoadToLEA();
+
// Patch the offset into the propoerty map to load from, then patch the Structure to look for.
stubInfo->hotPathBegin.dataLabelPtrAtOffset(patchOffsetPutByIdStructure).repatch(structure);
- stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetPutByIdPropertyMapOffset).repatch(cachedOffset * sizeof(JSValue));
+ stubInfo->hotPathBegin.dataLabel32AtOffset(patchOffsetPutByIdPropertyMapOffset).repatch(offset);
}
void JIT::privateCompilePatchGetArrayLength(ProcessorReturnAddress returnAddress)
Jump failureCases2 = checkStructure(regT0, structure);
// Checks out okay! - getDirectOffset
- loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_propertyStorage)), regT0);
- loadPtr(Address(regT0, cachedOffset * sizeof(JSValue)), regT0);
+ compileGetDirectOffset(regT0, regT0, structure, cachedOffset);
ret();
Call failureCases1Call = makeTailRecursiveCall(failureCases1);
#endif
// Checks out okay! - getDirectOffset
- PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
- loadPtr(static_cast<void*>(protoPropertyStorage), regT1);
- loadPtr(Address(regT1, cachedOffset * sizeof(JSValue)), regT0);
+ compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
Jump success = jump();
Jump failureCases3 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure));
// Checks out okay! - getDirectOffset
- PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
- loadPtr(protoPropertyStorage, regT1);
- loadPtr(Address(regT1, cachedOffset * sizeof(JSValue)), regT0);
+ compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
ret();
void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, size_t cachedOffset)
{
Jump failureCase = checkStructure(regT0, structure);
- loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_propertyStorage)), regT0);
- loadPtr(Address(regT0, cachedOffset * sizeof(JSValue)), regT0);
+ compileGetDirectOffset(regT0, regT0, structure, cachedOffset);
Jump success = jump();
void* code = m_assembler.executableCopy(m_codeBlock->executablePool());
#endif
// Checks out okay! - getDirectOffset
- PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
- loadPtr(protoPropertyStorage, regT1);
- loadPtr(Address(regT1, cachedOffset * sizeof(JSValue)), regT0);
+ compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
Jump success = jump();
}
ASSERT(protoObject);
- PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
- loadPtr(protoPropertyStorage, regT1);
- loadPtr(Address(regT1, cachedOffset * sizeof(JSValue)), regT0);
+ compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
Jump success = jump();
void* code = m_assembler.executableCopy(m_codeBlock->executablePool());
}
ASSERT(protoObject);
- PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
- loadPtr(protoPropertyStorage, regT1);
- loadPtr(Address(regT1, cachedOffset * sizeof(JSValue)), regT0);
+ compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
Jump success = jump();
void* code = m_assembler.executableCopy(m_codeBlock->executablePool());
}
ASSERT(protoObject);
- PropertyStorage* protoPropertyStorage = &protoObject->m_propertyStorage;
- loadPtr(protoPropertyStorage, regT1);
- loadPtr(Address(regT1, cachedOffset * sizeof(JSValue)), regT0);
+ compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
+ compilePutDirectOffset(regT0, regT1, structure, cachedOffset);
ret();
void* code = m_assembler.executableCopy(m_codeBlock->executablePool());
Jump failureCases2 = checkStructure(regT0, structure);
// checks out okay! - putDirectOffset
- loadPtr(Address(regT0, FIELD_OFFSET(JSObject, m_propertyStorage)), regT0);
- storePtr(regT1, Address(regT0, cachedOffset * sizeof(JSValue)));
+ compilePutDirectOffset(regT0, regT1, structure, cachedOffset);
ret();
Call failureCases1Call = makeTailRecursiveCall(failureCases1);
JSCell::mark();
m_structure->mark();
+ PropertyStorage storage = propertyStorage();
+
size_t storageSize = m_structure->propertyStorageSize();
for (size_t i = 0; i < storageSize; ++i) {
- JSValue v = m_propertyStorage[i];
+ JSValue v = JSValue::decode(storage[i]);
if (!v.marked())
v.mark();
}
if (m_structure->isDictionary()) {
offset = m_structure->removePropertyWithoutTransition(propertyName);
if (offset != WTF::notFound)
- m_propertyStorage[offset] = jsUndefined();
+ putDirectOffset(offset, jsUndefined());
return;
}
RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
- if (offset != WTF::notFound)
- m_propertyStorage[offset] = jsUndefined();
setStructure(structure.release());
+ if (offset != WTF::notFound)
+ putDirectOffset(offset, jsUndefined());
}
void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
Function = 1 << 4, // property is a function - only used by static hashtables
};
- typedef JSValue* PropertyStorage;
+ typedef EncodedJSValue* PropertyStorage;
+ typedef EncodedJSValue const * ConstPropertyStorage;
class JSObject : public JSCell {
friend class BatchedTransitionOptimizer;
void setStructure(PassRefPtr<Structure>);
Structure* inheritorID();
- PropertyStorage& propertyStorage() { return m_propertyStorage; }
+ ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); }
+ PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); }
virtual UString className() const;
JSValue getDirect(const Identifier& propertyName) const
{
size_t offset = m_structure->get(propertyName);
- return offset != WTF::notFound ? m_propertyStorage[offset] : JSValue();
+ return offset != WTF::notFound ? getDirectOffset(offset) : JSValue();
+ }
+
+ size_t getOffset(const Identifier& propertyName)
+ {
+ return m_structure->get(propertyName);
}
JSValue* getDirectLocation(const Identifier& propertyName)
return offset != WTF::notFound ? locationForOffset(offset) : 0;
}
- size_t offsetForLocation(JSValue* location)
+ size_t offsetForLocation(JSValue* location) const
+ {
+ return location - reinterpret_cast<JSValue const *>(propertyStorage());
+ }
+
+ JSValue const * locationForOffset(size_t offset) const
{
- return location - m_propertyStorage;
+ return reinterpret_cast<JSValue const *>(&propertyStorage()[offset]);
}
JSValue* locationForOffset(size_t offset)
{
- return &m_propertyStorage[offset];
+ return reinterpret_cast<JSValue*>(&propertyStorage()[offset]);
}
void transitionTo(Structure*);
void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0);
// Fast access to known property offsets.
- JSValue getDirectOffset(size_t offset) { return m_propertyStorage[offset]; }
- void putDirectOffset(size_t offset, JSValue value) { m_propertyStorage[offset] = value; }
+ JSValue getDirectOffset(size_t offset) const { return JSValue::decode(propertyStorage()[offset]); }
+ void putDirectOffset(size_t offset, JSValue value) { propertyStorage()[offset] = JSValue::encode(value); }
void fillGetterPropertySlot(PropertySlot&, JSValue* location);
void allocatePropertyStorage(size_t oldSize, size_t newSize);
void allocatePropertyStorageInline(size_t oldSize, size_t newSize);
- bool usingInlineStorage() const { return m_propertyStorage == m_inlineStorage; }
+ bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); }
- static const size_t inlineStorageCapacity = 2;
+ static const size_t inlineStorageCapacity = 3;
static const size_t nonInlineBaseStorageCapacity = 16;
static PassRefPtr<Structure> createStructure(JSValue prototype)
RefPtr<Structure> m_inheritorID;
- PropertyStorage m_propertyStorage;
- JSValue m_inlineStorage[inlineStorageCapacity];
+ union {
+ PropertyStorage m_externalStorage;
+ EncodedJSValue m_inlineStorage[inlineStorageCapacity];
+ };
};
JSObject* asObject(JSValue);
inline JSObject::JSObject(PassRefPtr<Structure> structure)
: JSCell(structure.releaseRef()) // ~JSObject balances this ref()
- , m_propertyStorage(m_inlineStorage)
{
ASSERT(m_structure);
ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity);
inline JSObject::~JSObject()
{
ASSERT(m_structure);
- if (m_propertyStorage != m_inlineStorage)
- delete [] m_propertyStorage;
+ if (!isUsingInlineStorage())
+ delete [] m_externalStorage;
m_structure->deref();
}
return createInheritorID();
}
+inline bool Structure::isUsingInlineStorage() const
+{
+ return (propertyStorageCapacity() == JSObject::inlineStorageCapacity);
+}
+
inline bool JSCell::isObject(const ClassInfo* info) const
{
for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
if (offset != WTF::notFound) {
if (checkReadOnly && currentAttributes & ReadOnly)
return;
- m_propertyStorage[offset] = value;
+ putDirectOffset(offset, value);
slot.setExistingProperty(this, offset);
return;
}
allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
ASSERT(offset < m_structure->propertyStorageCapacity());
- m_propertyStorage[offset] = value;
+ putDirectOffset(offset, value);
slot.setNewProperty(this, offset);
return;
}
allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
ASSERT(offset < structure->propertyStorageCapacity());
- m_propertyStorage[offset] = value;
+ setStructure(structure.release());
+ putDirectOffset(offset, value);
slot.setNewProperty(this, offset);
slot.setWasTransition(true);
- setStructure(structure.release());
return;
}
if (offset != WTF::notFound) {
if (checkReadOnly && currentAttributes & ReadOnly)
return;
- m_propertyStorage[offset] = value;
+ putDirectOffset(offset, value);
slot.setExistingProperty(this, offset);
return;
}
allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
ASSERT(offset < structure->propertyStorageCapacity());
- m_propertyStorage[offset] = value;
+ setStructure(structure.release());
+ putDirectOffset(offset, value);
slot.setNewProperty(this, offset);
slot.setWasTransition(true);
- setStructure(structure.release());
}
inline void JSObject::putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attributes)
size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes);
if (currentCapacity != m_structure->propertyStorageCapacity())
allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
- m_propertyStorage[offset] = value;
+ putDirectOffset(offset, value);
}
inline void JSObject::transitionTo(Structure* newStructure)
{
ASSERT(newSize > oldSize);
- JSValue* oldPropertyStorage = m_propertyStorage;
- m_propertyStorage = new JSValue[newSize];
+ // It's important that this function not rely on m_structure, since
+ // we might be in the middle of a transition.
+ bool wasInline = (oldSize == JSObject::inlineStorageCapacity);
+
+ PropertyStorage oldPropertyStorage = (wasInline ? m_inlineStorage : m_externalStorage);
+ PropertyStorage newPropertyStorage = new EncodedJSValue[newSize];
for (unsigned i = 0; i < oldSize; ++i)
- m_propertyStorage[i] = oldPropertyStorage[i];
+ newPropertyStorage[i] = oldPropertyStorage[i];
- if (oldPropertyStorage != m_inlineStorage)
+ if (!wasInline)
delete [] oldPropertyStorage;
+
+ m_externalStorage = newPropertyStorage;
}
} // namespace JSC
void growPropertyStorageCapacity();
size_t propertyStorageCapacity() const { return m_propertyStorageCapacity; }
size_t propertyStorageSize() const { return m_propertyTable ? m_propertyTable->keyCount + (m_propertyTable->deletedOffsets ? m_propertyTable->deletedOffsets->size() : 0) : m_offset + 1; }
+ bool isUsingInlineStorage() const;
size_t get(const Identifier& propertyName);
size_t get(const Identifier& propertyName, unsigned& attributes);