Commit 1a826c84 authored by oliver@apple.com's avatar oliver@apple.com

2010-02-18 Oliver Hunt <oliver@apple.com>

        Reviewed by Gavin Barraclough.

        Improve interpreter getter performance
        https://bugs.webkit.org/show_bug.cgi?id=35138

        Improve the performance of getter dispatch by making it possible
        for the interpreter to cache the GetterSetter object lookup.

        To do this we simply need to make PropertySlot aware of getters
        as a potentially cacheable property, and record the base and this
        objects for a getter access.  This allows us to use more-or-less
        identical code to that used by the normal get_by_id caching, with
        the dispatch being the only actual difference.

        I'm holding off of implementing this in the JIT until I do some
        cleanup to try and making coding in the JIT not be as horrible
        as it is currently.

        * bytecode/CodeBlock.cpp:
        (JSC::CodeBlock::dump):
        (JSC::CodeBlock::derefStructures):
        (JSC::CodeBlock::refStructures):
        * bytecode/Opcode.h:
        * interpreter/Interpreter.cpp:
        (JSC::Interpreter::resolveGlobal):
        (JSC::Interpreter::tryCacheGetByID):
        (JSC::Interpreter::privateExecute):
        * jit/JIT.cpp:
        (JSC::JIT::privateCompileMainPass):
        * jit/JITStubs.cpp:
        (JSC::JITThunks::tryCacheGetByID):
        (JSC::DEFINE_STUB_FUNCTION):
        * runtime/JSObject.cpp:
        (JSC::JSObject::fillGetterPropertySlot):
        * runtime/PropertySlot.cpp:
        (JSC::PropertySlot::functionGetter):
        * runtime/PropertySlot.h:
        (JSC::PropertySlot::isGetter):
        (JSC::PropertySlot::isCacheable):
        (JSC::PropertySlot::isCacheableValue):
        (JSC::PropertySlot::setValueSlot):
        (JSC::PropertySlot::setGetterSlot):
        (JSC::PropertySlot::setCacheableGetterSlot):
        (JSC::PropertySlot::clearOffset):
        (JSC::PropertySlot::thisValue):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@55002 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 4dc2c648
2010-02-18 Oliver Hunt <oliver@apple.com>
Reviewed by Gavin Barraclough.
Improve interpreter getter performance
https://bugs.webkit.org/show_bug.cgi?id=35138
Improve the performance of getter dispatch by making it possible
for the interpreter to cache the GetterSetter object lookup.
To do this we simply need to make PropertySlot aware of getters
as a potentially cacheable property, and record the base and this
objects for a getter access. This allows us to use more-or-less
identical code to that used by the normal get_by_id caching, with
the dispatch being the only actual difference.
I'm holding off of implementing this in the JIT until I do some
cleanup to try and making coding in the JIT not be as horrible
as it is currently.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
(JSC::CodeBlock::derefStructures):
(JSC::CodeBlock::refStructures):
* bytecode/Opcode.h:
* interpreter/Interpreter.cpp:
(JSC::Interpreter::resolveGlobal):
(JSC::Interpreter::tryCacheGetByID):
(JSC::Interpreter::privateExecute):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JITStubs.cpp:
(JSC::JITThunks::tryCacheGetByID):
(JSC::DEFINE_STUB_FUNCTION):
* runtime/JSObject.cpp:
(JSC::JSObject::fillGetterPropertySlot):
* runtime/PropertySlot.cpp:
(JSC::PropertySlot::functionGetter):
* runtime/PropertySlot.h:
(JSC::PropertySlot::isGetter):
(JSC::PropertySlot::isCacheable):
(JSC::PropertySlot::isCacheableValue):
(JSC::PropertySlot::setValueSlot):
(JSC::PropertySlot::setGetterSlot):
(JSC::PropertySlot::setCacheableGetterSlot):
(JSC::PropertySlot::clearOffset):
(JSC::PropertySlot::thisValue):
2010-02-17 Geoffrey Garen <ggaren@apple.com>
Reviewed by Oliver Hunt.
......
......@@ -765,6 +765,26 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
printGetByIdOp(exec, location, it, "get_by_id_chain");
break;
}
case op_get_by_id_getter_self: {
printGetByIdOp(exec, location, it, "get_by_id_getter_self");
break;
}
case op_get_by_id_getter_self_list: {
printGetByIdOp(exec, location, it, "get_by_id_getter_self_list");
break;
}
case op_get_by_id_getter_proto: {
printGetByIdOp(exec, location, it, "get_by_id_getter_proto");
break;
}
case op_get_by_id_getter_proto_list: {
printGetByIdOp(exec, location, it, "get_by_id_getter_proto_list");
break;
}
case op_get_by_id_getter_chain: {
printGetByIdOp(exec, location, it, "get_by_id_getter_chain");
break;
}
case op_get_by_id_generic: {
printGetByIdOp(exec, location, it, "get_by_id_generic");
break;
......@@ -1355,16 +1375,16 @@ void CodeBlock::derefStructures(Instruction* vPC) const
{
Interpreter* interpreter = m_globalData->interpreter;
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_self)) {
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_self) || vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_getter_self)) {
vPC[4].u.structure->deref();
return;
}
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto)) {
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto) || vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_getter_proto)) {
vPC[4].u.structure->deref();
vPC[5].u.structure->deref();
return;
}
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_chain)) {
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_chain) || vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_getter_chain)) {
vPC[4].u.structure->deref();
vPC[5].u.structureChain->deref();
return;
......@@ -1385,7 +1405,9 @@ void CodeBlock::derefStructures(Instruction* vPC) const
return;
}
if ((vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto_list))
|| (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_self_list))) {
|| (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_self_list))
|| (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_getter_proto_list))
|| (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_getter_self_list))) {
PolymorphicAccessStructureList* polymorphicStructures = vPC[4].u.polymorphicStructures;
polymorphicStructures->derefStructures(vPC[5].u.operand);
delete polymorphicStructures;
......@@ -1400,16 +1422,16 @@ void CodeBlock::refStructures(Instruction* vPC) const
{
Interpreter* interpreter = m_globalData->interpreter;
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_self)) {
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_self) || vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_getter_self)) {
vPC[4].u.structure->ref();
return;
}
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto)) {
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto) || vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_getter_proto)) {
vPC[4].u.structure->ref();
vPC[5].u.structure->ref();
return;
}
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_chain)) {
if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_chain) || vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_getter_chain)) {
vPC[4].u.structure->ref();
vPC[5].u.structureChain->ref();
return;
......
......@@ -104,6 +104,11 @@ namespace JSC {
macro(op_get_by_id_proto, 8) \
macro(op_get_by_id_proto_list, 8) \
macro(op_get_by_id_chain, 8) \
macro(op_get_by_id_getter_self, 8) \
macro(op_get_by_id_getter_self_list, 8) \
macro(op_get_by_id_getter_proto, 8) \
macro(op_get_by_id_getter_proto_list, 8) \
macro(op_get_by_id_getter_chain, 8) \
macro(op_get_by_id_generic, 8) \
macro(op_get_array_length, 8) \
macro(op_get_string_length, 8) \
......
This diff is collapsed.
......@@ -322,6 +322,11 @@ void JIT::privateCompileMainPass()
case op_get_by_id_proto_list:
case op_get_by_id_self:
case op_get_by_id_self_list:
case op_get_by_id_getter_chain:
case op_get_by_id_getter_proto:
case op_get_by_id_getter_proto_list:
case op_get_by_id_getter_self:
case op_get_by_id_getter_self_list:
case op_get_string_length:
case op_put_by_id_generic:
case op_put_by_id_replace:
......
......@@ -856,10 +856,11 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
}
// Uncacheable: give up.
if (!slot.isCacheable()) {
if (!slot.isCacheableValue()) {
ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_generic));
return;
}
ASSERT(!slot.isGetter());
JSCell* baseCell = asCell(baseValue);
Structure* structure = baseCell->structure();
......@@ -1289,7 +1290,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_method_check)
// If we successfully got something, then the base from which it is being accessed must
// be an object. (Assertion to ensure asObject() call below is safe, which comes after
// an isCacheable() chceck.
ASSERT(!slot.isCacheable() || slot.slotBase().isObject());
ASSERT(!slot.isCacheableValue() || slot.slotBase().isObject());
// Check that:
// * We're dealing with a JSCell,
......@@ -1300,7 +1301,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_method_check)
JSCell* specific;
JSObject* slotBaseObject;
if (baseValue.isCell()
&& slot.isCacheable()
&& slot.isCacheableValue()
&& !(structure = asCell(baseValue)->structure())->isUncacheableDictionary()
&& (slotBaseObject = asObject(slot.slotBase()))->getPropertySpecificValue(callFrame, ident, specific)
&& specific
......@@ -1374,7 +1375,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_self_fail)
CHECK_FOR_EXCEPTION();
if (baseValue.isCell()
&& slot.isCacheable()
&& slot.isCacheableValue()
&& !asCell(baseValue)->structure()->isUncacheableDictionary()
&& slot.slotBase() == baseValue) {
......@@ -1447,7 +1448,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list)
CHECK_FOR_EXCEPTION();
if (!baseValue.isCell() || !slot.isCacheable() || asCell(baseValue)->structure()->isDictionary()) {
if (!baseValue.isCell() || !slot.isCacheableValue() || asCell(baseValue)->structure()->isDictionary()) {
ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_fail));
return JSValue::encode(result);
}
......@@ -2303,7 +2304,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_resolve_global)
PropertySlot slot(globalObject);
if (globalObject->getPropertySlot(callFrame, ident, slot)) {
JSValue result = slot.getValue(callFrame, ident);
if (slot.isCacheable() && !globalObject->structure()->isUncacheableDictionary() && slot.slotBase() == globalObject) {
if (slot.isCacheableValue() && !globalObject->structure()->isUncacheableDictionary() && slot.slotBase() == globalObject) {
GlobalResolveInfo& globalResolveInfo = callFrame->codeBlock()->globalResolveInfo(globalResolveInfoIndex);
if (globalResolveInfo.structure)
globalResolveInfo.structure->deref();
......
......@@ -516,9 +516,12 @@ void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunct
NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
{
if (JSObject* getterFunction = asGetterSetter(*location)->getter())
slot.setGetterSlot(getterFunction);
else
if (JSObject* getterFunction = asGetterSetter(*location)->getter()) {
if (!structure()->isDictionary())
slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
else
slot.setGetterSlot(getterFunction);
} else
slot.setUndefined();
}
......
......@@ -35,10 +35,10 @@ JSValue PropertySlot::functionGetter(ExecState* exec, const Identifier&, const P
CallData callData;
CallType callType = slot.m_data.getterFunc->getCallData(callData);
if (callType == CallTypeHost)
return callData.native.function(exec, slot.m_data.getterFunc, slot.slotBase(), exec->emptyList());
return callData.native.function(exec, slot.m_data.getterFunc, slot.thisValue(), exec->emptyList());
ASSERT(callType == CallTypeJS);
// FIXME: Can this be done more efficiently using the callData?
return asFunction(slot.m_data.getterFunc)->call(exec, slot.slotBase(), exec->emptyList());
return asFunction(slot.m_data.getterFunc)->call(exec, slot.thisValue(), exec->emptyList());
}
} // namespace JSC
......@@ -71,7 +71,9 @@ namespace JSC {
return m_getValue(exec, Identifier::from(exec, propertyName), *this);
}
bool isCacheable() const { return m_offset != WTF::notFound; }
bool isGetter() const { return m_isGetter; }
bool isCacheable() const { return m_isCacheable; }
bool isCacheableValue() const { return m_isCacheable && !m_isGetter; }
size_t cachedOffset() const
{
ASSERT(isCacheable());
......@@ -102,6 +104,8 @@ namespace JSC {
m_slotBase = slotBase;
m_data.valueSlot = valueSlot;
m_offset = offset;
m_isCacheable = true;
m_isGetter = false;
}
void setValue(JSValue value)
......@@ -139,14 +143,28 @@ namespace JSC {
m_slotBase = slotBase;
m_data.index = index;
}
void setGetterSlot(JSObject* getterFunc)
{
ASSERT(getterFunc);
m_thisValue = m_slotBase;
m_getValue = functionGetter;
m_data.getterFunc = getterFunc;
m_isGetter = true;
}
void setCacheableGetterSlot(JSValue slotBase, JSObject* getterFunc, unsigned offset)
{
ASSERT(getterFunc);
m_getValue = functionGetter;
m_thisValue = m_slotBase;
m_slotBase = slotBase;
m_data.getterFunc = getterFunc;
m_offset = offset;
m_isCacheable = true;
m_isGetter = true;
}
void setUndefined()
{
setValue(jsUndefined());
......@@ -182,11 +200,14 @@ namespace JSC {
{
// Clear offset even in release builds, in case this PropertySlot has been used before.
// (For other data members, we don't need to clear anything because reuse would meaningfully overwrite them.)
m_offset = WTF::notFound;
m_offset = 0;
m_isCacheable = false;
m_isGetter = false;
}
unsigned index() const { return m_data.index; }
JSValue thisValue() const { return m_thisValue; }
private:
static JSValue functionGetter(ExecState*, const Identifier&, const PropertySlot&);
......@@ -201,8 +222,11 @@ namespace JSC {
} m_data;
JSValue m_value;
JSValue m_thisValue;
size_t m_offset;
bool m_isCacheable : 1;
bool m_isGetter : 1;
};
} // namespace JSC
......
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