+2008-09-13 Oliver Hunt <oliver@apple.com>
+
+ Reviewed by Cameron Zwarich.
+
+ Bug 20821: Cache property transitions to speed up object initialization
+ https://bugs.webkit.org/show_bug.cgi?id=20821
+
+ Implement a transition cache to improve the performance of new properties
+ being added to objects. This is extremely beneficial in constructors and
+ shows up as a 34% improvement on access-binary-trees in SunSpider (0.8%
+ overall)
+
+ * VM/CTI.cpp:
+ (JSC::CTI::privateCompileMainPass):
+ (JSC::):
+ (JSC::transitionWillNeedStorageRealloc):
+ (JSC::CTI::privateCompilePutByIdTransition):
+ * VM/CTI.h:
+ (JSC::CTI::compilePutByIdTransition):
+ * VM/CodeBlock.cpp:
+ (JSC::printPutByIdOp):
+ (JSC::CodeBlock::printStructureIDs):
+ (JSC::CodeBlock::dump):
+ (JSC::CodeBlock::derefStructureIDs):
+ (JSC::CodeBlock::refStructureIDs):
+ * VM/CodeGenerator.cpp:
+ (JSC::CodeGenerator::emitPutById):
+ * VM/Machine.cpp:
+ (JSC::cachePrototypeChain):
+ (JSC::Machine::tryCachePutByID):
+ (JSC::Machine::tryCacheGetByID):
+ (JSC::Machine::privateExecute):
+ (JSC::Machine::tryCTICachePutByID):
+ (JSC::Machine::tryCTICacheGetByID):
+ * VM/Machine.h:
+ * VM/Opcode.h:
+ * kjs/JSObject.h:
+ (JSC::JSObject::putDirect):
+ (JSC::JSObject::transitionTo):
+ * kjs/PutPropertySlot.h:
+ (JSC::PutPropertySlot::PutPropertySlot):
+ (JSC::PutPropertySlot::wasTransition):
+ (JSC::PutPropertySlot::setWasTransition):
+ * kjs/StructureID.cpp:
+ (JSC::StructureID::transitionTo):
+ (JSC::StructureIDChain::StructureIDChain):
+ * kjs/StructureID.h:
+ (JSC::StructureID::previousID):
+ (JSC::StructureID::setCachedPrototypeChain):
+ (JSC::StructureID::cachedPrototypeChain):
+ (JSC::StructureID::propertyMap):
+ * masm/X86Assembler.h:
+ (JSC::X86Assembler::addl_i8m):
+ (JSC::X86Assembler::subl_i8m):
+
2008-09-12 Cameron Zwarich <cwzwarich@uwaterloo.ca>
Reviewed by Maciej Stachowiak.
emitPutArg(X86::eax, 0); // leave the base in eax
emitPutArg(X86::edx, 8); // leave the base in edx
emitCall(i, Machine::cti_op_put_by_id);
- i += 6;
+ i += 8;
break;
}
case op_get_by_id: {
case op_get_string_length:
case op_put_by_id_generic:
case op_put_by_id_replace:
+ case op_put_by_id_transition:
ASSERT_NOT_REACHED();
}
}
return code;
}
+extern "C" {
+
+static JSValue* SFX_CALL transitionObject(StructureID* newStructureID, size_t cachedOffset, JSObject* baseObject, JSValue* value)
+{
+ StructureID* oldStructureID = newStructureID->previousID();
+
+ baseObject->transitionTo(newStructureID);
+
+ if (oldStructureID->propertyMap().storageSize() == JSObject::inlineStorageCapacity)
+ baseObject->allocatePropertyStorage(oldStructureID->propertyMap().storageSize(), oldStructureID->propertyMap().size());
+
+ baseObject->putDirectOffset(cachedOffset, value);
+ return baseObject;
+}
+
+}
+
+static inline bool transitionWillNeedStorageRealloc(StructureID* oldStructureID, StructureID* newStructureID)
+{
+ if (oldStructureID->propertyMap().storageSize() == JSObject::inlineStorageCapacity)
+ return true;
+
+ if (oldStructureID->propertyMap().storageSize() < JSObject::inlineStorageCapacity)
+ return false;
+
+ if (oldStructureID->propertyMap().size() != newStructureID->propertyMap().size())
+ return true;
+
+ return false;
+}
+
+void* CTI::privateCompilePutByIdTransition(StructureID* oldStructureID, StructureID* newStructureID, size_t cachedOffset, StructureIDChain* sIDC)
+{
+ Vector<X86Assembler::JmpSrc, 16> failureCases;
+ // check eax is an object of the right StructureID.
+ m_jit.testl_i32r(JSImmediate::TagMask, X86::eax);
+ failureCases.append(m_jit.emitUnlinkedJne());
+ m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(oldStructureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
+ failureCases.append(m_jit.emitUnlinkedJne());
+ Vector<X86Assembler::JmpSrc> successCases;
+
+ // ecx = baseObject
+ m_jit.movl_mr(OBJECT_OFFSET(JSCell, m_structureID), X86::eax, X86::ecx);
+ // proto(ecx) = baseObject->structureID()->prototype()
+ m_jit.cmpl_i32m(ObjectType, OBJECT_OFFSET(StructureID, m_type), X86::ecx);
+ failureCases.append(m_jit.emitUnlinkedJne());
+ m_jit.movl_mr(OBJECT_OFFSET(StructureID, m_prototype), X86::ecx, X86::ecx);
+
+ // ecx = baseObject->m_structureID
+ for (RefPtr<StructureID>* it = sIDC->head(); *it; ++it) {
+ // null check the prototype
+ m_jit.cmpl_i32r(reinterpret_cast<intptr_t> (jsNull()), X86::ecx);
+ successCases.append(m_jit.emitUnlinkedJe());
+
+ // Check the structure id
+ m_jit.cmpl_i32m(reinterpret_cast<uint32_t>(it->get()), OBJECT_OFFSET(JSCell, m_structureID), X86::ecx);
+ failureCases.append(m_jit.emitUnlinkedJne());
+
+ m_jit.movl_mr(OBJECT_OFFSET(JSCell, m_structureID), X86::ecx, X86::ecx);
+ m_jit.cmpl_i32m(ObjectType, OBJECT_OFFSET(StructureID, m_type), X86::ecx);
+ failureCases.append(m_jit.emitUnlinkedJne());
+ m_jit.movl_mr(OBJECT_OFFSET(StructureID, m_prototype), X86::ecx, X86::ecx);
+ }
+
+ failureCases.append(m_jit.emitUnlinkedJne());
+ for (unsigned i = 0; i < successCases.size(); ++i)
+ m_jit.link(successCases[i], m_jit.label());
+
+ X86Assembler::JmpSrc callTarget;
+ // Fast case, don't need to do any heavy lifting, so don't bother making a call.
+ if (!transitionWillNeedStorageRealloc(oldStructureID, newStructureID)) {
+ // Assumes m_refCount can be decremented easily, refcount decrement is safe as
+ // codeblock should ensure oldStructureID->m_refCount > 0
+ m_jit.subl_i8m(1, reinterpret_cast<void*>(oldStructureID));
+ m_jit.addl_i8m(1, reinterpret_cast<void*>(newStructureID));
+ m_jit.movl_i32m(reinterpret_cast<uint32_t>(newStructureID), OBJECT_OFFSET(JSCell, m_structureID), X86::eax);
+
+ // write the value
+ m_jit.movl_mr(OBJECT_OFFSET(JSObject, m_propertyStorage), X86::eax, X86::eax);
+ m_jit.movl_rm(X86::edx, cachedOffset * sizeof(JSValue*), X86::eax);
+ } else {
+ // Slow case transition -- we're going to need to quite a bit of work,
+ // so just make a call
+ m_jit.pushl_r(X86::edx);
+ m_jit.pushl_r(X86::eax);
+ m_jit.movl_i32r(cachedOffset, X86::eax);
+ m_jit.pushl_r(X86::eax);
+ m_jit.movl_i32r(reinterpret_cast<uint32_t>(newStructureID), X86::eax);
+ m_jit.pushl_r(X86::eax);
+ callTarget = m_jit.emitCall();
+ m_jit.addl_i32r(4 * sizeof(void*), X86::esp);
+ }
+ m_jit.ret();
+ void* code = m_jit.copy();
+ ASSERT(code);
+
+ for (unsigned i = 0; i < failureCases.size(); ++i)
+ X86Assembler::link(code, failureCases[i], reinterpret_cast<void*>(Machine::cti_op_put_by_id_fail));
+
+ if (transitionWillNeedStorageRealloc(oldStructureID, newStructureID))
+ X86Assembler::link(code, callTarget, reinterpret_cast<void*>(transitionObject));
+
+ m_codeBlock->structureIDAccessStubs.append(code);
+
+ return code;
+}
+
void* CTI::privateArrayLengthTrampoline()
{
// Check eax is an array
CTI cti(machine, exec, codeBlock);
return cti.privateCompilePutByIdReplace(structureID, cachedOffset);
}
+
+ static void* compilePutByIdTransition(Machine* machine, ExecState* exec, CodeBlock* codeBlock, StructureID* oldStructureID, StructureID* newStructureID, size_t cachedOffset, StructureIDChain* sIDC)
+ {
+ CTI cti(machine, exec, codeBlock);
+ return cti.privateCompilePutByIdTransition(oldStructureID, newStructureID, cachedOffset, sIDC);
+ }
static void* compileArrayLengthTrampoline(Machine* machine, ExecState* exec, CodeBlock* codeBlock)
{
void* privateCompileGetByIdProto(ExecState*, StructureID*, StructureID* prototypeStructureID, size_t cachedOffset);
void* privateCompileGetByIdChain(ExecState*, StructureID*, StructureIDChain*, size_t count, size_t cachedOffset);
void* privateCompilePutByIdReplace(StructureID*, size_t cachedOffset);
+ void* privateCompilePutByIdTransition(StructureID*, StructureID*, size_t cachedOffset, StructureIDChain*);
void* privateArrayLengthTrampoline();
void* privateStringLengthTrampoline();
int id0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printf("[%4d] %s\t %s, %s, %s\n", location, op, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), registerName(r1).c_str());
- it += 2;
+ it += 4;
}
void CodeBlock::printStructureID(const char* name, const Instruction* vPC, int operand) const
printf(" [%4d] %s: %s, %s\n", instructionOffset, "get_by_id_proto", pointerToSourceString(vPC[4].u.structureID).UTF8String().c_str(), pointerToSourceString(vPC[5].u.structureID).UTF8String().c_str());
return;
}
+ if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_transition)) {
+ printf(" [%4d] %s: %s, %s, %s\n", instructionOffset, "put_by_id_new", pointerToSourceString(vPC[4].u.structureID).UTF8String().c_str(), pointerToSourceString(vPC[5].u.structureID).UTF8String().c_str(), pointerToSourceString(vPC[6].u.structureIDChain).UTF8String().c_str());
+ return;
+ }
if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_chain)) {
printf(" [%4d] %s: %s, %s\n", instructionOffset, "get_by_id_chain", pointerToSourceString(vPC[4].u.structureID).UTF8String().c_str(), pointerToSourceString(vPC[5].u.structureIDChain).UTF8String().c_str());
return;
printPutByIdOp(location, it, identifiers, "put_by_id_replace");
break;
}
+ case op_put_by_id_transition: {
+ printPutByIdOp(location, it, identifiers, "put_by_id_transition");
+ break;
+ }
case op_put_by_id_generic: {
printPutByIdOp(location, it, identifiers, "put_by_id_generic");
break;
vPC[5].u.structureIDChain->deref();
return;
}
+ if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_transition)) {
+ vPC[4].u.structureID->deref();
+ vPC[5].u.structureID->deref();
+ vPC[6].u.structureIDChain->deref();
+ return;
+ }
if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_replace)) {
vPC[4].u.structureID->deref();
return;
vPC[5].u.structureIDChain->ref();
return;
}
+ if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_transition)) {
+ vPC[4].u.structureID->ref();
+ vPC[5].u.structureID->ref();
+ vPC[6].u.structureIDChain->ref();
+ return;
+ }
if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_replace)) {
vPC[4].u.structureID->ref();
return;
instructions().append(value->index());
instructions().append(0);
instructions().append(0);
+ instructions().append(0);
+ instructions().append(0);
return value;
}
static StructureIDChain* cachePrototypeChain(ExecState* exec, StructureID* structureID)
{
- RefPtr<StructureIDChain> chain = StructureIDChain::create(static_cast<JSObject*>(structureID->prototypeForLookup(exec))->structureID());
+ JSValue* prototype = structureID->prototypeForLookup(exec);
+ if (JSImmediate::isImmediate(prototype))
+ return 0;
+ RefPtr<StructureIDChain> chain = StructureIDChain::create(static_cast<JSObject*>(prototype)->structureID());
structureID->setCachedPrototypeChain(chain.release());
return structureID->cachedPrototypeChain();
}
-NEVER_INLINE void Machine::tryCachePutByID(CodeBlock* codeBlock, Instruction* vPC, JSValue* baseValue, const PutPropertySlot& slot)
+NEVER_INLINE void Machine::tryCachePutByID(ExecState* exec, CodeBlock* codeBlock, Instruction* vPC, JSValue* baseValue, const PutPropertySlot& slot)
{
// Recursive invocation may already have specialized this instruction.
if (vPC[0].u.opcode != getOpcode(op_put_by_id))
vPC[0] = getOpcode(op_put_by_id_generic);
return;
}
-
- // FIXME: Cache new property transitions, too.
- if (slot.type() == PutPropertySlot::NewProperty) {
- vPC[0] = getOpcode(op_put_by_id_generic);
- return;
- }
JSCell* baseCell = static_cast<JSCell*>(baseValue);
StructureID* structureID = baseCell->structureID();
vPC[0] = getOpcode(op_put_by_id_generic);
return;
}
+
+ // StructureID transition, cache transition info
+ if (slot.type() == PutPropertySlot::NewProperty) {
+ vPC[0] = getOpcode(op_put_by_id_transition);
+ vPC[4] = structureID->previousID();
+ vPC[5] = structureID;
+ StructureIDChain* chain = structureID->cachedPrototypeChain();
+ if (!chain) {
+ chain = cachePrototypeChain(exec, structureID);
+ if (!chain) {
+ // This happens if someone has manually inserted null into the prototype chain
+ vPC[0] = getOpcode(op_put_by_id_generic);
+ return;
+ }
+ }
+ vPC[6] = chain;
+ vPC[7] = slot.cachedOffset();
+ codeBlock->refStructureIDs(vPC);
+ return;
+ }
+
vPC[0] = getOpcode(op_put_by_id_replace);
vPC[5] = slot.cachedOffset();
codeBlock->refStructureIDs(vPC);
StructureIDChain* chain = structureID->cachedPrototypeChain();
if (!chain)
chain = cachePrototypeChain(exec, structureID);
+ ASSERT(chain);
vPC[0] = getOpcode(op_get_by_id_chain);
vPC[4] = structureID;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_put_by_id) {
- /* put_by_id base(r) property(id) value(r) nop(n) nop(n)
+ /* put_by_id base(r) property(id) value(r) nop(n) nop(n) nop(n) nop(n)
Generic property access: Sets the property named by identifier
property, belonging to register base, to register value.
baseValue->put(exec, ident, r[value].jsValue(exec), slot);
VM_CHECK_EXCEPTION();
- tryCachePutByID(codeBlock, vPC, baseValue, slot);
+ tryCachePutByID(exec, codeBlock, vPC, baseValue, slot);
- vPC += 6;
+ vPC += 8;
+ NEXT_OPCODE;
+ }
+ BEGIN_OPCODE(op_put_by_id_transition) {
+ /* op_put_by_id_transition base(r) property(id) value(r) oldStructureID(sID) newStructureID(sID) structureIDChain(sIDc) offset(n)
+
+ Cached property access: Attempts to set a new property with a cached transition
+ property named by identifier property, belonging to register base,
+ to register value. If the cache misses, op_put_by_id_transition
+ reverts to op_put_by_id_generic.
+
+ Unlike many opcodes, this one does not write any output to
+ the register file.
+ */
+ int base = vPC[1].u.operand;
+ JSValue* baseValue = r[base].jsValue(exec);
+
+ if (LIKELY(!JSImmediate::isImmediate(baseValue))) {
+ JSCell* baseCell = static_cast<JSCell*>(baseValue);
+ StructureID* oldStructureID = vPC[4].u.structureID;
+ StructureID* newStructureID = vPC[5].u.structureID;
+
+ if (LIKELY(baseCell->structureID() == oldStructureID)) {
+ ASSERT(baseCell->isObject());
+ JSObject* baseObject = static_cast<JSObject*>(baseCell);
+
+ RefPtr<StructureID>* it = vPC[6].u.structureIDChain->head();
+
+ JSObject* proto = static_cast<JSObject*>(baseObject->structureID()->prototypeForLookup(exec));
+ while (!proto->isNull()) {
+ if (UNLIKELY(proto->structureID() != (*it).get())) {
+ uncachePutByID(codeBlock, vPC);
+ NEXT_OPCODE;
+ }
+ ++it;
+ proto = static_cast<JSObject*>(proto->structureID()->prototypeForLookup(exec));
+ }
+
+ baseObject->transitionTo(newStructureID);
+ if (oldStructureID->propertyMap().storageSize() == JSObject::inlineStorageCapacity)
+ baseObject->allocatePropertyStorage(oldStructureID->propertyMap().storageSize(), oldStructureID->propertyMap().size());
+
+ int value = vPC[3].u.operand;
+ unsigned offset = vPC[7].u.operand;
+ ASSERT(baseObject->offsetForLocation(baseObject->getDirectLocation(codeBlock->identifiers[vPC[2].u.operand])) == offset);
+ baseObject->putDirectOffset(offset, r[value].jsValue(exec));
+
+ vPC += 8;
+ NEXT_OPCODE;
+ }
+ }
+
+ uncachePutByID(codeBlock, vPC);
NEXT_OPCODE;
}
BEGIN_OPCODE(op_put_by_id_replace) {
- /* op_put_by_id_replace base(r) property(id) value(r) structureID(sID) offset(n)
+ /* op_put_by_id_replace base(r) property(id) value(r) structureID(sID) offset(n) nop(n) nop(n)
Cached property access: Attempts to set a pre-existing, cached
property named by identifier property, belonging to register base,
ASSERT(baseObject->offsetForLocation(baseObject->getDirectLocation(codeBlock->identifiers[vPC[2].u.operand])) == offset);
baseObject->putDirectOffset(offset, r[value].jsValue(exec));
- vPC += 6;
+ vPC += 8;
NEXT_OPCODE;
}
}
NEXT_OPCODE;
}
BEGIN_OPCODE(op_put_by_id_generic) {
- /* op_put_by_id_generic base(r) property(id) value(r) nop(n) nop(n)
+ /* op_put_by_id_generic base(r) property(id) value(r) nop(n) nop(n) nop(n) nop(n)
Generic property access: Sets the property named by identifier
property, belonging to register base, to register value.
baseValue->put(exec, ident, r[value].jsValue(exec), slot);
VM_CHECK_EXCEPTION();
- vPC += 6;
+ vPC += 8;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_del_by_id) {
ctiRepatchCallByReturnAddress(returnAddress, (void*)cti_op_put_by_id_generic);
return;
}
-
- // FIXME: Cache new property transitions, too.
- if (slot.type() == PutPropertySlot::NewProperty) {
- ctiRepatchCallByReturnAddress(returnAddress, (void*)cti_op_put_by_id_generic);
- return;
- }
JSCell* baseCell = static_cast<JSCell*>(baseValue);
StructureID* structureID = baseCell->structureID();
ctiRepatchCallByReturnAddress(returnAddress, (void*)cti_op_put_by_id_generic);
return;
}
+
+ // StructureID transition, cache transition info
+ if (slot.type() == PutPropertySlot::NewProperty) {
+ vPC[0] = getOpcode(op_put_by_id_transition);
+ vPC[4] = structureID->previousID();
+ vPC[5] = structureID;
+ StructureIDChain* chain = structureID->cachedPrototypeChain();
+ if (!chain) {
+ chain = cachePrototypeChain(exec, structureID);
+ if (!chain) {
+ // This happens if someone has manually inserted null into the prototype chain
+ vPC[0] = getOpcode(op_put_by_id_generic);
+ return;
+ }
+ }
+ vPC[6] = chain;
+ vPC[7] = slot.cachedOffset();
+ codeBlock->refStructureIDs(vPC);
+ ctiRepatchCallByReturnAddress(returnAddress, CTI::compilePutByIdTransition(this, exec, codeBlock, structureID->previousID(), structureID, slot.cachedOffset(), chain));
+ return;
+ }
+
vPC[0] = getOpcode(op_put_by_id_replace);
vPC[4] = structureID;
vPC[5] = slot.cachedOffset();
if (!chain)
chain = cachePrototypeChain(exec, structureID);
+ ASSERT(chain);
vPC[0] = getOpcode(op_get_by_id_chain);
vPC[4] = structureID;
vPC[5] = chain;
void tryCacheGetByID(ExecState*, CodeBlock*, Instruction* vPC, JSValue* baseValue, const Identifier& propertyName, const PropertySlot&);
void uncacheGetByID(CodeBlock*, Instruction* vPC);
- void tryCachePutByID(CodeBlock*, Instruction* vPC, JSValue* baseValue, const PutPropertySlot&);
+ void tryCachePutByID(ExecState* exec, CodeBlock*, Instruction* vPC, JSValue* baseValue, const PutPropertySlot&);
void uncachePutByID(CodeBlock*, Instruction* vPC);
#if ENABLE(CTI)
macro(op_get_array_length) \
macro(op_get_string_length) \
macro(op_put_by_id) \
+ macro(op_put_by_id_transition) \
macro(op_put_by_id_replace) \
macro(op_put_by_id_generic) \
macro(op_del_by_id) \
return &m_propertyStorage[offset];
}
+ void transitionTo(StructureID*);
+
void removeDirect(const Identifier& propertyName);
bool hasCustomProperties() { return !m_structureID->propertyMap().isEmpty(); }
bool hasGetterSetterProperties() { return m_structureID->propertyMap().hasGetterSetterProperties(); }
void allocatePropertyStorage(size_t oldSize, size_t newSize);
bool usingInlineStorage() const { return m_propertyStorage == m_inlineStorage; }
+ static const size_t inlineStorageCapacity = 2;
+
protected:
bool getOwnPropertySlotForWrite(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable);
const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
StructureID* createInheritorID();
- static const size_t inlineStorageCapacity = 2;
-
RefPtr<StructureID> m_inheritorID;
PropertyStorage m_propertyStorage;
return;
}
- unsigned currentAttributes;
- size_t offset = m_structureID->propertyMap().getOffset(propertyName, currentAttributes);
- if (offset != WTF::notFound) {
- if (checkReadOnly && currentAttributes & ReadOnly)
- return;
- m_propertyStorage[offset] = value;
- slot.setExistingProperty(this, offset);
- return;
- }
+ unsigned currentAttributes;
+ size_t offset = m_structureID->propertyMap().getOffset(propertyName, currentAttributes);
+ if (offset != WTF::notFound) {
+ if (checkReadOnly && currentAttributes & ReadOnly)
+ return;
+ m_propertyStorage[offset] = value;
+ slot.setExistingProperty(this, offset);
+ return;
+ }
if (m_structureID->propertyMap().storageSize() == inlineStorageCapacity)
allocatePropertyStorage(m_structureID->propertyMap().storageSize(), m_structureID->propertyMap().size());
RefPtr<StructureID> structureID = StructureID::addPropertyTransition(m_structureID, propertyName, value, attributes, this, slot, m_propertyStorage);
+ slot.setWasTransition(true);
setStructureID(structureID.release());
}
+inline void JSObject::transitionTo(StructureID* newStructureID)
+{
+ StructureID::transitionTo(m_structureID, newStructureID, this);
+ setStructureID(newStructureID);
+}
+
inline JSValue* JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
{
return defaultValue(exec, preferredType);
PutPropertySlot()
: m_type(Invalid)
, m_base(0)
+ , m_wasTransition(false)
{
}
ASSERT(isCacheable());
return m_offset;
}
-
+
+ bool wasTransition() const { return m_wasTransition; }
+ void setWasTransition(bool wasTransition) { m_wasTransition = wasTransition; }
private:
Type m_type;
JSObject* m_base;
+ bool m_wasTransition;
size_t m_offset;
};
ASSERT(m_prototype->isObject() || m_prototype->isNull());
}
+void StructureID::transitionTo(StructureID* oldStructureID, StructureID* newStructureID, JSObject* slotBase)
+{
+ if (!slotBase->usingInlineStorage() && oldStructureID->m_propertyMap.size() != newStructureID->m_propertyMap.size())
+ slotBase->allocatePropertyStorage(oldStructureID->m_propertyMap.size(), newStructureID->m_propertyMap.size());
+}
+
PassRefPtr<StructureID> StructureID::addPropertyTransition(StructureID* structureID, const Identifier& propertyName, JSValue* value, unsigned attributes, JSObject* slotBase, PutPropertySlot& slot, PropertyStorage& propertyStorage)
{
ASSERT(!structureID->m_isDictionary);
tmp = static_cast<JSCell*>(tmp->storedPrototype())->structureID();
}
- m_vector.set(new RefPtr<StructureID>[size]);
+ m_vector.set(new RefPtr<StructureID>[size + 1]);
size_t i;
for (i = 0; i < size - 1; ++i) {
structureID = static_cast<JSObject*>(structureID->storedPrototype())->structureID();
}
m_vector[i] = structureID;
+ m_vector[i + 1] = 0;
}
} // namespace JSC
class StructureID : public RefCounted<StructureID> {
public:
+ friend class CTI;
static PassRefPtr<StructureID> create(JSValue* prototype, JSType type = ObjectType)
{
return adoptRef(new StructureID(prototype, type));
JSValue* storedPrototype() const { return m_prototype; }
JSValue* prototypeForLookup(ExecState*);
-
+
+ StructureID* previousID() const { return m_previous.get(); }
+
void setCachedPrototypeChain(PassRefPtr<StructureIDChain> cachedPrototypeChain) { m_cachedPrototypeChain = cachedPrototypeChain; }
StructureIDChain* cachedPrototypeChain() const { return m_cachedPrototypeChain.get(); }
const PropertyMap& propertyMap() const { return m_propertyMap; }
PropertyMap& propertyMap() { return m_propertyMap; }
+ static void transitionTo(StructureID* oldStructureID, StructureID* newStructureID, JSObject* slotBase);
+
private:
typedef std::pair<RefPtr<UString::Rep>, unsigned> TransitionTableKey;
typedef HashMap<TransitionTableKey, StructureID*, TransitionTableHash, TransitionTableHashTraits> TransitionTable;
m_buffer->putByte(imm);
}
+ void addl_i8m(int imm, void* addr)
+ {
+ m_buffer->putByte(OP_GROUP1_EvIb);
+ emitModRm_opm(GROUP1_OP_ADD, addr);
+ m_buffer->putByte(imm);
+ }
+
void addl_i32r(int imm, RegisterID dst)
{
m_buffer->putByte(OP_GROUP1_EvIz);
emitModRm_opr(GROUP1_OP_SUB, dst);
m_buffer->putByte(imm);
}
+
+ void subl_i8m(int imm, void* addr)
+ {
+ m_buffer->putByte(OP_GROUP1_EvIb);
+ emitModRm_opm(GROUP1_OP_SUB, addr);
+ m_buffer->putByte(imm);
+ }
void subl_i32r(int imm, RegisterID dst)
{