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

2009-05-21 Gavin Barraclough <barraclough@apple.com>

        Reviewed by Geoff Garen.

        op_method_check

        Optimize method calls, by caching specific function values within the Structure.
        The new opcode is used almost like an x86 opcode prefix byte to optimize op_get_by_id,
        where the property access is being used to read a function to be passed to op-call (i.e.
        'foo.bar();').  This patch modifies the Structure class such that when a property is
        put to an object for the first time we will check if the value is a function.  If it is,
        we will cache the function value on the Structure.  A Structure in such a state guarantees
        that not only does a property with the given identifier exist on the object, but also that
        its value is unchanged.  Upon any further attempt to put a property with the same identifier
        (but a different value) to the object, it will transition back to a normal Structure (where
        it will guarantee the presence but not the value of the property).

        op_method_check makes use of the new information made available by the Structure, by
        augmenting the functionality of op_get_by_id.  Upon generating a FunctionCallDotNode a
        check will be emitted prior to the property access reading the function value, and the JIT
        will generate an extra (initially unlinked but patchable) set of checks prior to the regular
        JIT code for get_by_id.  The new code will do inline structure and prototype structure check
        (unlike a regular get_by_id, which can only handle 'self' accesses inline), and then performs
        an immediate load of the function value, rather than using memory accesses to load the value
        from the obejct's property storage array.  If the method check fails it will revert, or if
        the access is polymorphic, the op_get_by_id will continue to operate - and optimize itself -
        just as any other regular op_get_by_id would.

        ~2.5% on v8-tests, due to a ~9% progression on richards.

        * API/JSCallbackObjectFunctions.h:
        (JSC::::put):
        (JSC::::staticFunctionGetter):
        * API/JSObjectRef.cpp:
        (JSObjectMakeConstructor):
        * JavaScriptCore.exp:
        * assembler/AbstractMacroAssembler.h:
        (JSC::AbstractMacroAssembler::differenceBetween):
        * assembler/MacroAssemblerX86.h:
        (JSC::MacroAssemblerX86::moveWithPatch):
        * bytecode/CodeBlock.cpp:
        (JSC::CodeBlock::dump):
        * bytecode/CodeBlock.h:
        (JSC::getMethodCallLinkInfoReturnLocation):
        (JSC::CodeBlock::getMethodCallLinkInfo):
        (JSC::CodeBlock::addMethodCallLinkInfos):
        (JSC::CodeBlock::methodCallLinkInfo):
        * bytecode/Opcode.h:
        * bytecompiler/BytecodeGenerator.cpp:
        (JSC::BytecodeGenerator::emitMethodCheck):
        * bytecompiler/BytecodeGenerator.h:
        * interpreter/Interpreter.cpp:
        (JSC::Interpreter::privateExecute):
        * jit/JIT.cpp:
        (JSC::JIT::privateCompileMainPass):
        (JSC::JIT::privateCompileSlowCases):
        (JSC::JIT::privateCompile):
        * jit/JIT.h:
        (JSC::MethodCallCompilationInfo::MethodCallCompilationInfo):
        * jit/JITOpcodes.cpp:
        * jit/JITPropertyAccess.cpp:
        (JSC::JIT::emit_op_method_check):
        (JSC::JIT::emitSlow_op_method_check):
        (JSC::JIT::emit_op_get_by_id):
        (JSC::JIT::emitSlow_op_get_by_id):
        (JSC::JIT::emit_op_put_by_id):
        (JSC::JIT::emitSlow_op_put_by_id):
        (JSC::JIT::compileGetByIdHotPath):
        (JSC::JIT::compileGetByIdSlowCase):
        (JSC::JIT::patchMethodCallProto):
        * jit/JITStubs.cpp:
        (JSC::JITStubs::cti_op_get_by_id_method_check):
        (JSC::JITStubs::cti_op_get_by_id_method_check_second):
        * jit/JITStubs.h:
        * jsc.cpp:
        (GlobalObject::GlobalObject):
        * parser/Nodes.cpp:
        (JSC::FunctionCallDotNode::emitBytecode):
        * runtime/Arguments.cpp:
        (JSC::Arguments::put):
        * runtime/ArrayConstructor.cpp:
        (JSC::ArrayConstructor::ArrayConstructor):
        * runtime/BooleanConstructor.cpp:
        (JSC::BooleanConstructor::BooleanConstructor):
        * runtime/DateConstructor.cpp:
        (JSC::DateConstructor::DateConstructor):
        * runtime/ErrorConstructor.cpp:
        (JSC::ErrorConstructor::ErrorConstructor):
        (JSC::constructError):
        * runtime/ErrorPrototype.cpp:
        (JSC::ErrorPrototype::ErrorPrototype):
        * runtime/FunctionConstructor.cpp:
        (JSC::FunctionConstructor::FunctionConstructor):
        * runtime/FunctionPrototype.cpp:
        (JSC::FunctionPrototype::FunctionPrototype):
        * runtime/InternalFunction.cpp:
        (JSC::InternalFunction::InternalFunction):
        * runtime/JSActivation.cpp:
        (JSC::JSActivation::put):
        (JSC::JSActivation::putWithAttributes):
        * runtime/JSByteArray.cpp:
        (JSC::JSByteArray::JSByteArray):
        * runtime/JSFunction.cpp:
        (JSC::JSFunction::JSFunction):
        (JSC::JSFunction::getOwnPropertySlot):
        * runtime/JSGlobalObject.cpp:
        (JSC::JSGlobalObject::putWithAttributes):
        (JSC::JSGlobalObject::reset):
        (JSC::JSGlobalObject::mark):
        * runtime/JSGlobalObject.h:
        (JSC::JSGlobalObject::JSGlobalObjectData::JSGlobalObjectData):
        (JSC::JSGlobalObject::methodCallDummy):
        * runtime/JSObject.cpp:
        (JSC::JSObject::put):
        (JSC::JSObject::putWithAttributes):
        (JSC::JSObject::deleteProperty):
        (JSC::JSObject::defineGetter):
        (JSC::JSObject::defineSetter):
        (JSC::JSObject::getPropertyAttributes):
        (JSC::JSObject::getPropertySpecificFunction):
        (JSC::JSObject::putDirectFunction):
        (JSC::JSObject::putDirectFunctionWithoutTransition):
        * runtime/JSObject.h:
        (JSC::getJSFunction):
        (JSC::JSObject::getDirectLocation):
        (JSC::JSObject::putDirect):
        (JSC::JSObject::putDirectWithoutTransition):
        * runtime/LiteralParser.cpp:
        (JSC::LiteralParser::parseObject):
        * runtime/Lookup.cpp:
        (JSC::setUpStaticFunctionSlot):
        * runtime/Lookup.h:
        (JSC::lookupPut):
        * runtime/MathObject.cpp:
        (JSC::MathObject::MathObject):
        * runtime/NativeErrorConstructor.cpp:
        (JSC::NativeErrorConstructor::NativeErrorConstructor):
        (JSC::NativeErrorConstructor::construct):
        * runtime/NativeErrorPrototype.cpp:
        (JSC::NativeErrorPrototype::NativeErrorPrototype):
        * runtime/NumberConstructor.cpp:
        (JSC::NumberConstructor::NumberConstructor):
        * runtime/ObjectConstructor.cpp:
        (JSC::ObjectConstructor::ObjectConstructor):
        * runtime/PropertyMapHashTable.h:
        (JSC::PropertyMapEntry::PropertyMapEntry):
        * runtime/PrototypeFunction.cpp:
        (JSC::PrototypeFunction::PrototypeFunction):
        * runtime/PutPropertySlot.h:
        (JSC::PutPropertySlot::):
        (JSC::PutPropertySlot::PutPropertySlot):
        (JSC::PutPropertySlot::setNewProperty):
        (JSC::PutPropertySlot::setDespecifyFunctionProperty):
        (JSC::PutPropertySlot::isCacheable):
        (JSC::PutPropertySlot::cachedOffset):
        * runtime/RegExpConstructor.cpp:
        (JSC::RegExpConstructor::RegExpConstructor):
        * runtime/StringConstructor.cpp:
        (JSC::StringConstructor::StringConstructor):
        * runtime/StringPrototype.cpp:
        (JSC::StringPrototype::StringPrototype):
        * runtime/Structure.cpp:
        (JSC::Structure::Structure):
        (JSC::Structure::~Structure):
        (JSC::Structure::materializePropertyMap):
        (JSC::Structure::addPropertyTransitionToExistingStructure):
        (JSC::Structure::addPropertyTransition):
        (JSC::Structure::changeFunctionTransition):
        (JSC::Structure::addPropertyWithoutTransition):
        (JSC::Structure::get):
        (JSC::Structure::despecifyFunction):
        (JSC::Structure::put):
        (JSC::Structure::remove):
        * runtime/Structure.h:
        (JSC::Structure::get):
        (JSC::Structure::specificFunction):
        * runtime/StructureTransitionTable.h:
        (JSC::StructureTransitionTableHashTraits::emptyValue):
        * wtf/Platform.h:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@44076 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 25f2563c
2009-05-21 Gavin Barraclough <barraclough@apple.com>
Reviewed by Geoff Garen.
op_method_check
Optimize method calls, by caching specific function values within the Structure.
The new opcode is used almost like an x86 opcode prefix byte to optimize op_get_by_id,
where the property access is being used to read a function to be passed to op-call (i.e.
'foo.bar();'). This patch modifies the Structure class such that when a property is
put to an object for the first time we will check if the value is a function. If it is,
we will cache the function value on the Structure. A Structure in such a state guarantees
that not only does a property with the given identifier exist on the object, but also that
its value is unchanged. Upon any further attempt to put a property with the same identifier
(but a different value) to the object, it will transition back to a normal Structure (where
it will guarantee the presence but not the value of the property).
op_method_check makes use of the new information made available by the Structure, by
augmenting the functionality of op_get_by_id. Upon generating a FunctionCallDotNode a
check will be emitted prior to the property access reading the function value, and the JIT
will generate an extra (initially unlinked but patchable) set of checks prior to the regular
JIT code for get_by_id. The new code will do inline structure and prototype structure check
(unlike a regular get_by_id, which can only handle 'self' accesses inline), and then performs
an immediate load of the function value, rather than using memory accesses to load the value
from the obejct's property storage array. If the method check fails it will revert, or if
the access is polymorphic, the op_get_by_id will continue to operate - and optimize itself -
just as any other regular op_get_by_id would.
~2.5% on v8-tests, due to a ~9% progression on richards.
* API/JSCallbackObjectFunctions.h:
(JSC::::put):
(JSC::::staticFunctionGetter):
* API/JSObjectRef.cpp:
(JSObjectMakeConstructor):
* JavaScriptCore.exp:
* assembler/AbstractMacroAssembler.h:
(JSC::AbstractMacroAssembler::differenceBetween):
* assembler/MacroAssemblerX86.h:
(JSC::MacroAssemblerX86::moveWithPatch):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* bytecode/CodeBlock.h:
(JSC::getMethodCallLinkInfoReturnLocation):
(JSC::CodeBlock::getMethodCallLinkInfo):
(JSC::CodeBlock::addMethodCallLinkInfos):
(JSC::CodeBlock::methodCallLinkInfo):
* bytecode/Opcode.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitMethodCheck):
* bytecompiler/BytecodeGenerator.h:
* interpreter/Interpreter.cpp:
(JSC::Interpreter::privateExecute):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
(JSC::JIT::privateCompile):
* jit/JIT.h:
(JSC::MethodCallCompilationInfo::MethodCallCompilationInfo):
* jit/JITOpcodes.cpp:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_method_check):
(JSC::JIT::emitSlow_op_method_check):
(JSC::JIT::emit_op_get_by_id):
(JSC::JIT::emitSlow_op_get_by_id):
(JSC::JIT::emit_op_put_by_id):
(JSC::JIT::emitSlow_op_put_by_id):
(JSC::JIT::compileGetByIdHotPath):
(JSC::JIT::compileGetByIdSlowCase):
(JSC::JIT::patchMethodCallProto):
* jit/JITStubs.cpp:
(JSC::JITStubs::cti_op_get_by_id_method_check):
(JSC::JITStubs::cti_op_get_by_id_method_check_second):
* jit/JITStubs.h:
* jsc.cpp:
(GlobalObject::GlobalObject):
* parser/Nodes.cpp:
(JSC::FunctionCallDotNode::emitBytecode):
* runtime/Arguments.cpp:
(JSC::Arguments::put):
* runtime/ArrayConstructor.cpp:
(JSC::ArrayConstructor::ArrayConstructor):
* runtime/BooleanConstructor.cpp:
(JSC::BooleanConstructor::BooleanConstructor):
* runtime/DateConstructor.cpp:
(JSC::DateConstructor::DateConstructor):
* runtime/ErrorConstructor.cpp:
(JSC::ErrorConstructor::ErrorConstructor):
(JSC::constructError):
* runtime/ErrorPrototype.cpp:
(JSC::ErrorPrototype::ErrorPrototype):
* runtime/FunctionConstructor.cpp:
(JSC::FunctionConstructor::FunctionConstructor):
* runtime/FunctionPrototype.cpp:
(JSC::FunctionPrototype::FunctionPrototype):
* runtime/InternalFunction.cpp:
(JSC::InternalFunction::InternalFunction):
* runtime/JSActivation.cpp:
(JSC::JSActivation::put):
(JSC::JSActivation::putWithAttributes):
* runtime/JSByteArray.cpp:
(JSC::JSByteArray::JSByteArray):
* runtime/JSFunction.cpp:
(JSC::JSFunction::JSFunction):
(JSC::JSFunction::getOwnPropertySlot):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::putWithAttributes):
(JSC::JSGlobalObject::reset):
(JSC::JSGlobalObject::mark):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::JSGlobalObjectData::JSGlobalObjectData):
(JSC::JSGlobalObject::methodCallDummy):
* runtime/JSObject.cpp:
(JSC::JSObject::put):
(JSC::JSObject::putWithAttributes):
(JSC::JSObject::deleteProperty):
(JSC::JSObject::defineGetter):
(JSC::JSObject::defineSetter):
(JSC::JSObject::getPropertyAttributes):
(JSC::JSObject::getPropertySpecificFunction):
(JSC::JSObject::putDirectFunction):
(JSC::JSObject::putDirectFunctionWithoutTransition):
* runtime/JSObject.h:
(JSC::getJSFunction):
(JSC::JSObject::getDirectLocation):
(JSC::JSObject::putDirect):
(JSC::JSObject::putDirectWithoutTransition):
* runtime/LiteralParser.cpp:
(JSC::LiteralParser::parseObject):
* runtime/Lookup.cpp:
(JSC::setUpStaticFunctionSlot):
* runtime/Lookup.h:
(JSC::lookupPut):
* runtime/MathObject.cpp:
(JSC::MathObject::MathObject):
* runtime/NativeErrorConstructor.cpp:
(JSC::NativeErrorConstructor::NativeErrorConstructor):
(JSC::NativeErrorConstructor::construct):
* runtime/NativeErrorPrototype.cpp:
(JSC::NativeErrorPrototype::NativeErrorPrototype):
* runtime/NumberConstructor.cpp:
(JSC::NumberConstructor::NumberConstructor):
* runtime/ObjectConstructor.cpp:
(JSC::ObjectConstructor::ObjectConstructor):
* runtime/PropertyMapHashTable.h:
(JSC::PropertyMapEntry::PropertyMapEntry):
* runtime/PrototypeFunction.cpp:
(JSC::PrototypeFunction::PrototypeFunction):
* runtime/PutPropertySlot.h:
(JSC::PutPropertySlot::):
(JSC::PutPropertySlot::PutPropertySlot):
(JSC::PutPropertySlot::setNewProperty):
(JSC::PutPropertySlot::setDespecifyFunctionProperty):
(JSC::PutPropertySlot::isCacheable):
(JSC::PutPropertySlot::cachedOffset):
* runtime/RegExpConstructor.cpp:
(JSC::RegExpConstructor::RegExpConstructor):
* runtime/StringConstructor.cpp:
(JSC::StringConstructor::StringConstructor):
* runtime/StringPrototype.cpp:
(JSC::StringPrototype::StringPrototype):
* runtime/Structure.cpp:
(JSC::Structure::Structure):
(JSC::Structure::~Structure):
(JSC::Structure::materializePropertyMap):
(JSC::Structure::addPropertyTransitionToExistingStructure):
(JSC::Structure::addPropertyTransition):
(JSC::Structure::changeFunctionTransition):
(JSC::Structure::addPropertyWithoutTransition):
(JSC::Structure::get):
(JSC::Structure::despecifyFunction):
(JSC::Structure::put):
(JSC::Structure::remove):
* runtime/Structure.h:
(JSC::Structure::get):
(JSC::Structure::specificFunction):
* runtime/StructureTransitionTable.h:
(JSC::StructureTransitionTableHashTraits::emptyValue):
* wtf/Platform.h:
2009-05-22 Brent Fulgham <bfulgham@webkit.org>
Reviewed by Steve Falkenburg.
......@@ -244,6 +244,7 @@ __ZN3JSC8JSObject16getPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE
__ZN3JSC8JSObject17createInheritorIDEv
__ZN3JSC8JSObject17putDirectFunctionEPNS_9ExecStateEPNS_16InternalFunctionEj
__ZN3JSC8JSObject17putWithAttributesEPNS_9ExecStateERKNS_10IdentifierENS_7JSValueEj
__ZN3JSC8JSObject17putWithAttributesEPNS_9ExecStateERKNS_10IdentifierENS_7JSValueEjbRNS_15PutPropertySlotE
__ZN3JSC8JSObject17putWithAttributesEPNS_9ExecStateEjNS_7JSValueEj
__ZN3JSC8JSObject18getOwnPropertySlotEPNS_9ExecStateEjRNS_12PropertySlotE
__ZN3JSC8JSObject18getPrimitiveNumberEPNS_9ExecStateERdRNS_7JSValueE
......@@ -261,12 +262,13 @@ __ZN3JSC9CodeBlockD1Ev
__ZN3JSC9CodeBlockD2Ev
__ZN3JSC9Structure17stopIgnoringLeaksEv
__ZN3JSC9Structure18startIgnoringLeaksEv
__ZN3JSC9Structure21addPropertyTransitionEPS0_RKNS_10IdentifierEjRm
__ZN3JSC9Structure21addPropertyTransitionEPS0_RKNS_10IdentifierEjPNS_6JSCellERm
__ZN3JSC9Structure22materializePropertyMapEv
__ZN3JSC9Structure24changeFunctionTransitionEPS0_RKNS_10IdentifierE
__ZN3JSC9Structure25changePrototypeTransitionEPS0_NS_7JSValueE
__ZN3JSC9Structure28addPropertyWithoutTransitionERKNS_10IdentifierEj
__ZN3JSC9Structure3getERKNS_10IdentifierERj
__ZN3JSC9Structure40addPropertyTransitionToExistingStructureEPS0_RKNS_10IdentifierEjRm
__ZN3JSC9Structure28addPropertyWithoutTransitionERKNS_10IdentifierEjPNS_6JSCellE
__ZN3JSC9Structure3getEPKNS_7UString3RepERjRPNS_6JSCellE
__ZN3JSC9Structure40addPropertyTransitionToExistingStructureEPS0_RKNS_10IdentifierEjPNS_6JSCellERm
__ZN3JSC9StructureC1ENS_7JSValueERKNS_8TypeInfoE
__ZN3JSC9StructureD1Ev
__ZN3JSC9constructEPNS_9ExecStateENS_7JSValueENS_13ConstructTypeERKNS_13ConstructDataERKNS_7ArgListE
......
......@@ -872,6 +872,11 @@ public:
return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_jmp);
}
ptrdiff_t differenceBetween(DataLabelPtr from, DataLabelPtr to)
{
return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label);
}
ptrdiff_t differenceBetween(DataLabelPtr from, Call to)
{
return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_jmp);
......
......@@ -125,6 +125,12 @@ public:
}
DataLabelPtr moveWithPatch(ImmPtr initialValue, RegisterID dest)
{
m_assembler.movl_i32r(initialValue.asIntptr(), dest);
return DataLabelPtr(this);
}
Jump branchPtrWithPatch(Condition cond, RegisterID left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0))
{
m_assembler.cmpl_ir_force32(initialRightValue.asIntptr(), left);
......
......@@ -827,6 +827,10 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
printf("[%4d] put_setter\t %s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(r1).c_str());
break;
}
case op_method_check: {
printf("[%4d] op_method_check\n", location);
break;
}
case op_del_by_id: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
......@@ -1310,6 +1314,11 @@ CodeBlock::~CodeBlock()
callLinkInfo->callee->removeCaller(callLinkInfo);
}
for (size_t size = m_methodCallLinkInfos.size(), i = 0; i < size; ++i) {
if (Structure* structure = m_methodCallLinkInfos[i].cachedStructure)
structure->deref();
}
unlinkCallers();
#endif
......
......@@ -106,6 +106,17 @@ namespace JSC {
bool isLinked() { return callee; }
};
struct MethodCallLinkInfo {
MethodCallLinkInfo()
: cachedStructure(0)
{
}
MacroAssembler::CodeLocationCall callReturnLocation;
MacroAssembler::CodeLocationDataLabelPtr structureLabel;
Structure* cachedStructure;
};
struct FunctionRegisterInfo {
FunctionRegisterInfo(unsigned bytecodeOffset, int functionRegisterIndex)
: bytecodeOffset(bytecodeOffset)
......@@ -157,6 +168,11 @@ namespace JSC {
return callLinkInfo->callReturnLocation.calleeReturnAddressValue();
}
inline void* getMethodCallLinkInfoReturnLocation(MethodCallLinkInfo* methodCallLinkInfo)
{
return methodCallLinkInfo->callReturnLocation.calleeReturnAddressValue();
}
inline unsigned getCallReturnOffset(CallReturnOffsetToBytecodeIndex* pc)
{
return pc->callReturnOffset;
......@@ -281,6 +297,11 @@ namespace JSC {
return *(binaryChop<CallLinkInfo, void*, getCallLinkInfoReturnLocation>(m_callLinkInfos.begin(), m_callLinkInfos.size(), returnAddress));
}
MethodCallLinkInfo& getMethodCallLinkInfo(void* returnAddress)
{
return *(binaryChop<MethodCallLinkInfo, void*, getMethodCallLinkInfoReturnLocation>(m_methodCallLinkInfos.begin(), m_methodCallLinkInfos.size(), returnAddress));
}
unsigned getBytecodeIndex(CallFrame* callFrame, void* nativePC)
{
reparseForExceptionInfoIfNecessary(callFrame);
......@@ -344,6 +365,9 @@ namespace JSC {
void addCallLinkInfo() { m_callLinkInfos.append(CallLinkInfo()); }
CallLinkInfo& callLinkInfo(int index) { return m_callLinkInfos[index]; }
void addMethodCallLinkInfos(unsigned n) { m_methodCallLinkInfos.grow(n); }
MethodCallLinkInfo& methodCallLinkInfo(int index) { return m_methodCallLinkInfos[index]; }
void addFunctionRegisterInfo(unsigned bytecodeOffset, int functionIndex) { createRareDataIfNecessary(); m_rareData->m_functionRegisterInfos.append(FunctionRegisterInfo(bytecodeOffset, functionIndex)); }
#endif
......@@ -464,6 +488,7 @@ namespace JSC {
Vector<StructureStubInfo> m_structureStubInfos;
Vector<GlobalResolveInfo> m_globalResolveInfos;
Vector<CallLinkInfo> m_callLinkInfos;
Vector<MethodCallLinkInfo> m_methodCallLinkInfos;
Vector<CallLinkInfo*> m_linkedCallerList;
#endif
......
......@@ -147,6 +147,7 @@ namespace JSC {
macro(op_tear_off_activation, 2) \
macro(op_tear_off_arguments, 1) \
macro(op_ret, 2) \
macro(op_method_check, 1) \
\
macro(op_construct, 7) \
macro(op_construct_verify, 3) \
......
......@@ -1227,6 +1227,11 @@ RegisterID* BytecodeGenerator::emitResolveFunction(RegisterID* baseDst, Register
return baseDst;
}
void BytecodeGenerator::emitMethodCheck()
{
emitOpcode(op_method_check);
}
RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property)
{
#if ENABLE(JIT)
......
......@@ -280,6 +280,8 @@ namespace JSC {
RegisterID* emitResolveWithBase(RegisterID* baseDst, RegisterID* propDst, const Identifier& property);
RegisterID* emitResolveFunction(RegisterID* baseDst, RegisterID* funcDst, const Identifier& property);
void emitMethodCheck();
RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property);
RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value);
RegisterID* emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier&);
......
......@@ -3764,6 +3764,10 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
++vPC;
NEXT_INSTRUCTION();
}
DEFINE_OPCODE(op_method_check) {
vPC++;
NEXT_INSTRUCTION();
}
DEFINE_OPCODE(op_jsr) {
/* jsr retAddrDst(r) target(offset)
......
......@@ -216,6 +216,7 @@ void JIT::privateCompileMainPass()
DEFINE_OP(op_loop_if_lesseq)
DEFINE_OP(op_loop_if_true)
DEFINE_OP(op_lshift)
DEFINE_OP(op_method_check)
DEFINE_OP(op_mod)
DEFINE_OP(op_mov)
DEFINE_OP(op_mul)
......@@ -344,6 +345,7 @@ void JIT::privateCompileSlowCases()
DEFINE_SLOWCASE_OP(op_lshift)
DEFINE_SLOWCASE_OP(op_mod)
DEFINE_SLOWCASE_OP(op_mul)
DEFINE_SLOWCASE_OP(op_method_check)
DEFINE_SLOWCASE_OP(op_neq)
DEFINE_SLOWCASE_OP(op_not)
DEFINE_SLOWCASE_OP(op_nstricteq)
......@@ -485,6 +487,13 @@ void JIT::privateCompile()
info.coldPathOther = patchBuffer.locationOf(m_callStructureStubCompilationInfo[i].coldPathOther);
}
#endif
unsigned methodCallCount = m_methodCallCompilationInfo.size();
m_codeBlock->addMethodCallLinkInfos(methodCallCount);
for (unsigned i = 0; i < methodCallCount; ++i) {
MethodCallLinkInfo& info = m_codeBlock->methodCallLinkInfo(i);
info.structureLabel = patchBuffer.locationOf(m_methodCallCompilationInfo[i].structureToCompare);
info.callReturnLocation = m_codeBlock->structureStubInfo(m_methodCallCompilationInfo[i].propertyAccessIndex).callReturnLocation;
}
m_codeBlock->setJITCode(patchBuffer.finalizeCode());
}
......
......@@ -156,6 +156,16 @@ namespace JSC {
MacroAssembler::Label coldPathOther;
};
struct MethodCallCompilationInfo {
MethodCallCompilationInfo(unsigned propertyAccessIndex)
: propertyAccessIndex(propertyAccessIndex)
{
}
MacroAssembler::DataLabelPtr structureToCompare;
unsigned propertyAccessIndex;
};
void ctiPatchCallByReturnAddress(MacroAssembler::ProcessorReturnAddress returnAddress, void* newCalleeFunction);
void ctiPatchNearCallByReturnAddress(MacroAssembler::ProcessorReturnAddress returnAddress, void* newCalleeFunction);
......@@ -258,6 +268,10 @@ namespace JSC {
static const int patchOffsetGetByIdSlowCaseCall = 38 + ctiArgumentInitSize;
#endif
static const int patchOffsetOpCallCompareToJump = 9;
static const int patchOffsetMethodCheckProtoObj = 20;
static const int patchOffsetMethodCheckProtoStruct = 30;
static const int patchOffsetMethodCheckPutFunction = 50;
#else
// These architecture specific value are used to enable patching - see comment on op_put_by_id.
static const int patchOffsetPutByIdStructure = 7;
......@@ -279,6 +293,10 @@ namespace JSC {
static const int patchOffsetGetByIdSlowCaseCall = 21 + ctiArgumentInitSize;
#endif
static const int patchOffsetOpCallCompareToJump = 6;
static const int patchOffsetMethodCheckProtoObj = 11;
static const int patchOffsetMethodCheckProtoStruct = 18;
static const int patchOffsetMethodCheckPutFunction = 29;
#endif
public:
......@@ -330,6 +348,7 @@ namespace JSC {
static void patchGetByIdSelf(StructureStubInfo*, Structure*, size_t cachedOffset, ProcessorReturnAddress returnAddress);
static void patchPutByIdReplace(StructureStubInfo*, Structure*, size_t cachedOffset, ProcessorReturnAddress returnAddress);
static void patchMethodCallProto(MethodCallLinkInfo&, JSFunction*, Structure*, JSObject*);
static void compilePatchGetArrayLength(JSGlobalData* globalData, CodeBlock* codeBlock, ProcessorReturnAddress returnAddress)
{
......@@ -372,10 +391,10 @@ namespace JSC {
void addJump(Jump, int);
void emitJumpSlowToHot(Jump, int);
#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
void compileGetByIdHotPath(int resultVReg, int baseVReg, Identifier* ident, unsigned propertyAccessInstructionIndex);
void compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter, unsigned propertyAccessInstructionIndex);
void compilePutByIdHotPath(int baseVReg, Identifier* ident, int valueVReg, unsigned propertyAccessInstructionIndex);
void compilePutByIdSlowCase(int baseVReg, Identifier* ident, int valueVReg, Vector<SlowCaseEntry>::iterator& iter, unsigned propertyAccessInstructionIndex);
void compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter, unsigned propertyAccessInstructionIndex, bool isMethodCheck = false);
#endif
void compileOpCall(OpcodeID, Instruction* instruction, unsigned callLinkInfoIndex);
void compileOpCallVarargs(Instruction* instruction);
void compileOpCallInitializeCallFrame();
......@@ -440,6 +459,7 @@ namespace JSC {
void emit_op_new_func(Instruction*);
void emit_op_call(Instruction*);
void emit_op_call_eval(Instruction*);
void emit_op_method_check(Instruction*);
void emit_op_load_varargs(Instruction*);
void emit_op_call_varargs(Instruction*);
void emit_op_construct(Instruction*);
......@@ -525,6 +545,7 @@ namespace JSC {
void emitSlow_op_instanceof(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_call(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_call_eval(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_call_varargs(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_construct(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_to_jsnumber(Instruction*, Vector<SlowCaseEntry>::iterator&);
......@@ -648,6 +669,7 @@ namespace JSC {
Vector<Label> m_labels;
Vector<PropertyStubCompilationInfo> m_propertyAccessCompilationInfo;
Vector<StructureStubCompilationInfo> m_callStructureStubCompilationInfo;
Vector<MethodCallCompilationInfo> m_methodCallCompilationInfo;
Vector<JumpTable> m_jmpTable;
unsigned m_bytecodeIndex;
......
......@@ -996,16 +996,6 @@ void JIT::emitSlow_op_loop_if_less(Instruction* currentInstruction, Vector<SlowC
}
}
void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
compilePutByIdSlowCase(currentInstruction[1].u.operand, &(m_codeBlock->identifier(currentInstruction[2].u.operand)), currentInstruction[3].u.operand, iter, m_propertyAccessInstructionIndex++);
}
void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
compileGetByIdSlowCase(currentInstruction[1].u.operand, currentInstruction[2].u.operand, &(m_codeBlock->identifier(currentInstruction[3].u.operand)), iter, m_propertyAccessInstructionIndex++);
}
void JIT::emitSlow_op_loop_if_lesseq(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
unsigned op2 = currentInstruction[2].u.operand;
......
This diff is collapsed.
......@@ -690,6 +690,82 @@ EncodedJSValue JITStubs::cti_op_get_by_id(STUB_ARGS_DECLARATION)
return JSValue::encode(result);
}
EncodedJSValue JITStubs::cti_op_get_by_id_method_check(STUB_ARGS_DECLARATION)
{
STUB_INIT_STACK_FRAME(stackFrame);
CallFrame* callFrame = stackFrame.callFrame;
Identifier& ident = stackFrame.args[1].identifier();
JSValue baseValue = stackFrame.args[0].jsValue();
PropertySlot slot(baseValue);
JSValue result = baseValue.get(callFrame, ident, slot);
ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_method_check_second));
CHECK_FOR_EXCEPTION_AT_END();
return JSValue::encode(result);
}
EncodedJSValue JITStubs::cti_op_get_by_id_method_check_second(STUB_ARGS_DECLARATION)
{
STUB_INIT_STACK_FRAME(stackFrame);
CallFrame* callFrame = stackFrame.callFrame;
Identifier& ident = stackFrame.args[1].identifier();
JSValue baseValue = stackFrame.args[0].jsValue();
PropertySlot slot(baseValue);
JSValue result = baseValue.get(callFrame, ident, slot);
CHECK_FOR_EXCEPTION();
// 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());
// Check that:
// * We're dealing with a JSCell,
// * the property is cachable,
// * it's not a dictionary
// * there is a function cached.
Structure* structure;
JSCell* specific;
if (baseValue.isCell()
&& slot.isCacheable()
&& !(structure = asCell(baseValue)->structure())->isDictionary()
&& asObject(slot.slotBase())->getPropertySpecificValue(callFrame, ident, specific)
&& specific
) {
JSFunction* callee = (JSFunction*)specific;
// The result fetched should always be the callee!
ASSERT(result == JSValue(callee));
MethodCallLinkInfo& methodCallLinkInfo = callFrame->codeBlock()->getMethodCallLinkInfo(STUB_RETURN_ADDRESS);
// Check to see if the function is on the object's prototype. Patch up the code to optimize.
if (slot.slotBase() == structure->prototypeForLookup(callFrame))
JIT::patchMethodCallProto(methodCallLinkInfo, callee, structure, asObject(slot.slotBase()));
// Check to see if the function is on the object itself.
// Since we generate the method-check to check both the structure and a prototype-structure (since this
// is the common case) we have a problem - we need to patch the prototype structure check to do something
// useful. We could try to nop it out altogether, but that's a little messy, so lets do something simpler
// for now. For now it performs a check on a special object on the global object only used for this
// purpose. The object is in no way exposed, and as such the check will always pass.
else if (slot.slotBase() == baseValue)
JIT::patchMethodCallProto(methodCallLinkInfo, callee, structure, callFrame->scopeChain()->globalObject()->methodCallDummy());
// For now let any other case be cached as a normal get_by_id.
}
// Revert the get_by_id op back to being a regular get_by_id - allow it to cache like normal, if it needs to.
ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id));
return JSValue::encode(result);
}
EncodedJSValue JITStubs::cti_op_get_by_id_second(STUB_ARGS_DECLARATION)
{
STUB_INIT_STACK_FRAME(stackFrame);
......
......@@ -232,6 +232,8 @@ namespace JSC {
static EncodedJSValue JIT_STUB cti_op_div(STUB_ARGS_DECLARATION);
static EncodedJSValue JIT_STUB cti_op_eq(STUB_ARGS_DECLARATION);
static EncodedJSValue JIT_STUB cti_op_get_by_id(STUB_ARGS_DECLARATION);
static EncodedJSValue JIT_STUB cti_op_get_by_id_method_check(STUB_ARGS_DECLARATION);
static EncodedJSValue JIT_STUB cti_op_get_by_id_method_check_second(STUB_ARGS_DECLARATION);
static EncodedJSValue JIT_STUB cti_op_get_by_id_array_fail(STUB_ARGS_DECLARATION);
static EncodedJSValue JIT_STUB cti_op_get_by_id_generic(STUB_ARGS_DECLARATION);
static EncodedJSValue JIT_STUB cti_op_get_by_id_proto_fail(STUB_ARGS_DECLARATION);
......
......@@ -377,6 +377,7 @@ RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, Regi
{
RefPtr<RegisterID> base = generator.emitNode(m_base);
generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset);
generator.emitMethodCheck();
RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident);
RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get());
return generator.emitCall(generator.finalDestination(dst, function.get()), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset());
......
......@@ -118,7 +118,7 @@ void JSActivation::put(ExecState*, const Identifier& propertyName, JSValue value
}
// FIXME: Make this function honor ReadOnly (const) and DontEnum
void JSActivation::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes)
void JSActivation::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
{
ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
......@@ -130,7 +130,7 @@ void JSActivation::putWithAttributes(ExecState*, const Identifier& propertyName,
// expose in the activation object.
ASSERT(!hasGetterSetterProperties());
PutPropertySlot slot;
putDirect(propertyName, value, attributes, true, slot);
JSObject::putWithAttributes(exec, propertyName, value, attributes, true, slot);
}
bool JSActivation::deleteProperty(ExecState* exec, const Identifier& propertyName)
......
......@@ -170,7 +170,7 @@ void JSGlobalObject::putWithAttributes(ExecState* exec, const Identifier& proper
if (!valueBefore) {
JSValue valueAfter = getDirect(propertyName);
if (valueAfter)
putDirect(propertyName, valueAfter, attributes);
JSObject::putWithAttributes(exec, propertyName, valueAfter, attributes);
}
}
......@@ -239,6 +239,8 @@ void JSGlobalObject::reset(JSValue prototype)
d()->regExpPrototype = new (exec) RegExpPrototype(exec, RegExpPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get());
d()->regExpStructure = RegExpObject::createStructure(d()->regExpPrototype);
d()->methodCallDummy = constructEmptyObject(exec);
ErrorPrototype* errorPrototype = new (exec) ErrorPrototype(exec, ErrorPrototype::createStructure(d()->objectPrototype), d()->prototypeFunctionStructure.get());
d()->errorStructure = ErrorInstance::createStructure(errorPrototype);
......@@ -253,13 +255,13 @@ void JSGlobalObject::reset(JSValue prototype)
// Constructors
JSValue objectConstructor = new (exec) ObjectConstructor(exec, ObjectConstructor::createStructure(d()->functionPrototype), d()->objectPrototype);
JSValue functionConstructor = new (exec) FunctionConstructor(exec, FunctionConstructor::createStructure(d()->functionPrototype), d()->functionPrototype);
JSValue arrayConstructor = new (exec) ArrayConstructor(exec, ArrayConstructor::createStructure(d()->functionPrototype), d()->arrayPrototype);
JSValue stringConstructor = new (exec) StringConstructor(exec, StringConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->stringPrototype);
JSValue booleanConstructor = new (exec) BooleanConstructor(exec, BooleanConstructor::createStructure(d()->functionPrototype), d()->booleanPrototype);
JSValue numberConstructor = new (exec) NumberConstructor(exec, NumberConstructor::createStructure(d()->functionPrototype), d()->numberPrototype);
JSValue dateConstructor = new (exec) DateConstructor(exec, DateConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->datePrototype);
JSCell* objectConstructor = new (exec) ObjectConstructor(exec, ObjectConstructor::createStructure(d()->functionPrototype), d()->objectPrototype);
JSCell* functionConstructor = new (exec) FunctionConstructor(exec, FunctionConstructor::createStructure(d()->functionPrototype), d()->functionPrototype);
JSCell* arrayConstructor = new (exec) ArrayConstructor(exec, ArrayConstructor::createStructure(d()->functionPrototype), d()->arrayPrototype);
JSCell* stringConstructor = new (exec) StringConstructor(exec, StringConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->stringPrototype);
JSCell* booleanConstructor = new (exec) BooleanConstructor(exec, BooleanConstructor::createStructure(d()->functionPrototype), d()->booleanPrototype);
JSCell* numberConstructor = new (exec) NumberConstructor(exec, NumberConstructor::createStructure(d()->functionPrototype), d()->numberPrototype);
JSCell* dateConstructor = new (exec) DateConstructor(exec, DateConstructor::createStructure(d()->functionPrototype), d()->prototypeFunctionStructure.get(), d()->datePrototype);
d()->regExpConstructor = new (exec) RegExpConstructor(exec, RegExpConstructor::createStructure(d()->functionPrototype), d()->regExpPrototype);
......@@ -274,15 +276,15 @@ void JSGlobalObject::reset(JSValue prototype)
d()->typeErrorConstructor = new (exec) NativeErrorConstructor(exec, nativeErrorStructure, typeErrorPrototype);
d()->URIErrorConstructor = new (exec) NativeErrorConstructor(exec, nativeErrorStructure, URIErrorPrototype);
d()->objectPrototype->putDirectWithoutTransition(exec->propertyNames().constructor, objectConstructor, DontEnum);
d()->functionPrototype->putDirectWithoutTransition(exec->propertyNames().constructor