2008-10-17 Gavin Barraclough <barraclough@apple.com>
Optimize op_call by allowing call sites to be directly linked to callees.
For the hot path of op_call, CTI now generates a check (initially for an impossible
value), and the first time the call is executed we attempt to link the call directly
to the callee. WWe can currently only do so if the arity of the caller and callee
match. The (optimized) setup for the call on the hot path is linked directly to
the ctiCode for the callee, without indirection.
Two forms of the slow case of the call are generated, the first will be executed the
first time the call is reached. As well as this path attempting to link the call to
a callee, it also relinks the slow case to a second slow case, which will not continue
to attempt relinking the call. (This policy could be changed in future, but for not
this is intended to prevent thrashing).
If a callee that the caller has been linked to is garbage collected, then the link
in the caller's JIt code will be reset back to a value that cannot match - to prevent
any false positive matches.
~20% progression on deltablue & richards, >12% overall reduction in v8-tests
runtime, one or two percent progression on sunspider.
Reviewed by Oliver Hunt.
* VM/CTI.cpp:
(JSC::):
(JSC::CTI::emitNakedCall):
(JSC::unreachable):
(JSC::CTI::compileOpCallInitializeCallFrame):
(JSC::CTI::compileOpCallSetupArgs):
(JSC::CTI::compileOpCall):
(JSC::CTI::privateCompileMainPass):
(JSC::CTI::privateCompileSlowCases):
(JSC::CTI::privateCompile):
(JSC::CTI::unlinkCall):
(JSC::CTI::linkCall):
* VM/CTI.h:
* VM/CodeBlock.cpp:
(JSC::CodeBlock::~CodeBlock):
(JSC::CodeBlock::unlinkCallers):
(JSC::CodeBlock::derefStructureIDs):
* VM/CodeBlock.h:
(JSC::StructureStubInfo::StructureStubInfo):
(JSC::CallLinkInfo::CallLinkInfo):
(JSC::CodeBlock::addCaller):
(JSC::CodeBlock::removeCaller):
(JSC::CodeBlock::getStubInfo):
* VM/CodeGenerator.cpp:
(JSC::CodeGenerator::emitCall):
(JSC::CodeGenerator::emitConstruct):
* VM/Machine.cpp:
(JSC::Machine::cti_op_call_profiler):
(JSC::Machine::cti_op_call_JSFunction):
(JSC::Machine::cti_vm_lazyLinkCall):
(JSC::Machine::cti_op_construct_JSConstructFast):
(JSC::Machine::cti_op_construct_JSConstruct):
(JSC::Machine::cti_op_construct_NotJSConstruct):
* VM/Machine.h:
* kjs/JSFunction.cpp:
(JSC::JSFunction::~JSFunction):
* kjs/JSFunction.h:
* kjs/nodes.h:
(JSC::FunctionBodyNode::):
* masm/X86Assembler.h:
(JSC::X86Assembler::getDifferenceBetweenLabels):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@37670 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog
index b6d4cde..93aeb17 100644
--- a/JavaScriptCore/ChangeLog
+++ b/JavaScriptCore/ChangeLog
@@ -1,3 +1,70 @@
+2008-10-17 Gavin Barraclough <barraclough@apple.com>
+
+ Optimize op_call by allowing call sites to be directly linked to callees.
+
+ For the hot path of op_call, CTI now generates a check (initially for an impossible
+ value), and the first time the call is executed we attempt to link the call directly
+ to the callee. WWe can currently only do so if the arity of the caller and callee
+ match. The (optimized) setup for the call on the hot path is linked directly to
+ the ctiCode for the callee, without indirection.
+
+ Two forms of the slow case of the call are generated, the first will be executed the
+ first time the call is reached. As well as this path attempting to link the call to
+ a callee, it also relinks the slow case to a second slow case, which will not continue
+ to attempt relinking the call. (This policy could be changed in future, but for not
+ this is intended to prevent thrashing).
+
+ If a callee that the caller has been linked to is garbage collected, then the link
+ in the caller's JIt code will be reset back to a value that cannot match - to prevent
+ any false positive matches.
+
+ ~20% progression on deltablue & richards, >12% overall reduction in v8-tests
+ runtime, one or two percent progression on sunspider.
+
+ Reviewed by Oliver Hunt.
+
+ * VM/CTI.cpp:
+ (JSC::):
+ (JSC::CTI::emitNakedCall):
+ (JSC::unreachable):
+ (JSC::CTI::compileOpCallInitializeCallFrame):
+ (JSC::CTI::compileOpCallSetupArgs):
+ (JSC::CTI::compileOpCall):
+ (JSC::CTI::privateCompileMainPass):
+ (JSC::CTI::privateCompileSlowCases):
+ (JSC::CTI::privateCompile):
+ (JSC::CTI::unlinkCall):
+ (JSC::CTI::linkCall):
+ * VM/CTI.h:
+ * VM/CodeBlock.cpp:
+ (JSC::CodeBlock::~CodeBlock):
+ (JSC::CodeBlock::unlinkCallers):
+ (JSC::CodeBlock::derefStructureIDs):
+ * VM/CodeBlock.h:
+ (JSC::StructureStubInfo::StructureStubInfo):
+ (JSC::CallLinkInfo::CallLinkInfo):
+ (JSC::CodeBlock::addCaller):
+ (JSC::CodeBlock::removeCaller):
+ (JSC::CodeBlock::getStubInfo):
+ * VM/CodeGenerator.cpp:
+ (JSC::CodeGenerator::emitCall):
+ (JSC::CodeGenerator::emitConstruct):
+ * VM/Machine.cpp:
+ (JSC::Machine::cti_op_call_profiler):
+ (JSC::Machine::cti_op_call_JSFunction):
+ (JSC::Machine::cti_vm_lazyLinkCall):
+ (JSC::Machine::cti_op_construct_JSConstructFast):
+ (JSC::Machine::cti_op_construct_JSConstruct):
+ (JSC::Machine::cti_op_construct_NotJSConstruct):
+ * VM/Machine.h:
+ * kjs/JSFunction.cpp:
+ (JSC::JSFunction::~JSFunction):
+ * kjs/JSFunction.h:
+ * kjs/nodes.h:
+ (JSC::FunctionBodyNode::):
+ * masm/X86Assembler.h:
+ (JSC::X86Assembler::getDifferenceBetweenLabels):
+
2008-10-17 Maciej Stachowiak <mjs@apple.com>
Reviewed by Geoff Garen.
diff --git a/JavaScriptCore/VM/CTI.cpp b/JavaScriptCore/VM/CTI.cpp
index 3c4c3bb..7bd8e81 100644
--- a/JavaScriptCore/VM/CTI.cpp
+++ b/JavaScriptCore/VM/CTI.cpp
@@ -95,11 +95,13 @@
SYMBOL_STRING(ctiTrampoline) ":" "\n"
"pushl %esi" "\n"
"pushl %edi" "\n"
- "subl $0x24, %esp" "\n"
+ "pushl %ebx" "\n"
+ "subl $0x20, %esp" "\n"
"movl $512, %esi" "\n"
"movl 0x38(%esp), %edi" "\n" // Ox38 = 0x0E * 4, 0x0E = CTI_ARGS_callFrame (see assertion above)
"call *0x30(%esp)" "\n" // Ox30 = 0x0C * 4, 0x0C = CTI_ARGS_code (see assertion above)
- "addl $0x24, %esp" "\n"
+ "addl $0x20, %esp" "\n"
+ "popl %ebx" "\n"
"popl %edi" "\n"
"popl %esi" "\n"
"ret" "\n"
@@ -118,7 +120,8 @@
#else
"call " SYMBOL_STRING(_ZN3JSC7Machine12cti_vm_throwEPvz) "\n"
#endif
- "addl $0x24, %esp" "\n"
+ "addl $0x20, %esp" "\n"
+ "popl %ebx" "\n"
"popl %edi" "\n"
"popl %esi" "\n"
"ret" "\n"
@@ -133,12 +136,14 @@
__asm {
push esi;
push edi;
- sub esp, 0x24;
+ push ebx;
+ sub esp, 0x20;
mov esi, 512;
mov ecx, esp;
mov edi, [esp + 0x38];
call [esp + 0x30]; // Ox30 = 0x0C * 4, 0x0C = CTI_ARGS_code (see assertion above)
- add esp, 0x24;
+ add esp, 0x20;
+ pop ebx;
pop edi;
pop esi;
ret;
@@ -150,7 +155,8 @@
__asm {
mov ecx, esp;
call JSC::Machine::cti_vm_throw;
- add esp, 0x24;
+ add esp, 0x20;
+ pop ebx;
pop edi;
pop esi;
ret;
@@ -303,15 +309,21 @@
#endif
-ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCall(unsigned opcodeIndex, X86::RegisterID r)
+ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitNakedCall(unsigned opcodeIndex, X86::RegisterID r)
{
- m_jit.emitRestoreArgumentReference();
X86Assembler::JmpSrc call = m_jit.emitCall(r);
m_calls.append(CallRecord(call, opcodeIndex));
return call;
}
+ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitNakedCall(unsigned opcodeIndex, void(*function)())
+{
+ X86Assembler::JmpSrc call = m_jit.emitCall();
+ m_calls.append(CallRecord(call, reinterpret_cast<CTIHelper_v>(function), opcodeIndex));
+ return call;
+}
+
ALWAYS_INLINE X86Assembler::JmpSrc CTI::emitCTICall(unsigned opcodeIndex, CTIHelper_j helper)
{
#if ENABLE(SAMPLING_TOOL)
@@ -511,6 +523,12 @@
OpcodeID currentOpcodeID = static_cast<OpcodeID>(-1);
#endif
+static void unreachable()
+{
+ ASSERT_NOT_REACHED();
+ exit(1);
+}
+
void CTI::compileOpCallInitializeCallFrame(unsigned callee, unsigned argCount)
{
emitGetArg(callee, X86::ecx); // Load callee JSFunction into ecx
@@ -518,92 +536,103 @@
m_jit.movl_i32m(reinterpret_cast<unsigned>(nullJSValue), RegisterFile::OptionalCalleeArguments * static_cast<int>(sizeof(Register)), X86::edx);
m_jit.movl_rm(X86::ecx, RegisterFile::Callee * static_cast<int>(sizeof(Register)), X86::edx);
- m_jit.movl_mr(OBJECT_OFFSET(JSFunction, m_scopeChain) + OBJECT_OFFSET(ScopeChain, m_node), X86::ecx, X86::ecx); // newScopeChain
+ m_jit.movl_mr(OBJECT_OFFSET(JSFunction, m_scopeChain) + OBJECT_OFFSET(ScopeChain, m_node), X86::ecx, X86::ebx); // newScopeChain
m_jit.movl_i32m(argCount, RegisterFile::ArgumentCount * static_cast<int>(sizeof(Register)), X86::edx);
m_jit.movl_rm(X86::edi, RegisterFile::CallerFrame * static_cast<int>(sizeof(Register)), X86::edx);
- m_jit.movl_rm(X86::ecx, RegisterFile::ScopeChain * static_cast<int>(sizeof(Register)), X86::edx);
+ m_jit.movl_rm(X86::ebx, RegisterFile::ScopeChain * static_cast<int>(sizeof(Register)), X86::edx);
}
-void CTI::compileOpCall(Instruction* instruction, unsigned i, CompileOpCallType type)
+void CTI::compileOpCallSetupArgs(Instruction* instruction, bool isConstruct, bool isEval)
{
- int dst = instruction[i + 1].u.operand;
- int callee = instruction[i + 2].u.operand;
- int firstArg = instruction[i + 4].u.operand;
- int argCount = instruction[i + 5].u.operand;
- int registerOffset = instruction[i + 6].u.operand;
+ int firstArg = instruction[4].u.operand;
+ int argCount = instruction[5].u.operand;
+ int registerOffset = instruction[6].u.operand;
- if (type == OpCallEval)
- emitGetPutArg(instruction[i + 3].u.operand, 16, X86::ecx);
+ emitPutArg(X86::ecx, 0);
+ emitPutArgConstant(registerOffset, 4);
+ emitPutArgConstant(argCount, 8);
+ emitPutArgConstant(reinterpret_cast<unsigned>(instruction), 12);
+ if (isConstruct) {
+ emitGetPutArg(instruction[3].u.operand, 16, X86::eax);
+ emitPutArgConstant(firstArg, 20);
+ } else if (isEval)
+ emitGetPutArg(instruction[3].u.operand, 16, X86::eax);
+}
- if (type == OpConstruct) {
- emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 20);
- emitPutArgConstant(argCount, 16);
- emitPutArgConstant(registerOffset, 12);
- emitPutArgConstant(firstArg, 8);
- emitGetPutArg(instruction[i + 3].u.operand, 4, X86::ecx);
- } else {
- emitPutArgConstant(reinterpret_cast<unsigned>(instruction + i), 12);
- emitPutArgConstant(argCount, 8);
- emitPutArgConstant(registerOffset, 4);
+void CTI::compileOpCall(Instruction* instruction, unsigned i, unsigned structureIDInstructionIndex, CompileOpCallType type)
+{
+ int dst = instruction[1].u.operand;
+ int callee = instruction[2].u.operand;
+ int firstArg = instruction[4].u.operand;
+ int argCount = instruction[5].u.operand;
+ int registerOffset = instruction[6].u.operand;
- int thisVal = instruction[i + 3].u.operand;
+ // Setup this value as the first argument (does not apply to constructors)
+ if (type != OpConstruct) {
+ int thisVal = instruction[3].u.operand;
if (thisVal == missingThisObjectMarker()) {
// FIXME: should this be loaded dynamically off m_callFrame?
m_jit.movl_i32m(reinterpret_cast<unsigned>(m_callFrame->globalThisValue()), firstArg * sizeof(Register), X86::edi);
} else {
- emitGetArg(thisVal, X86::ecx);
- emitPutResult(firstArg, X86::ecx);
+ emitGetArg(thisVal, X86::eax);
+ emitPutResult(firstArg);
}
}
+ // Handle eval
X86Assembler::JmpSrc wasEval;
if (type == OpCallEval) {
- emitGetPutArg(callee, 0, X86::ecx);
- emitCTICall(i, Machine::cti_op_call_eval);
+ emitGetArg(callee, X86::ecx);
+ compileOpCallSetupArgs(instruction, false, true);
+ emitCTICall(i, Machine::cti_op_call_eval);
m_jit.cmpl_i32r(reinterpret_cast<unsigned>(JSImmediate::impossibleValue()), X86::eax);
wasEval = m_jit.emitUnlinkedJne();
-
- // this sets up the first arg to op_cti_call (func), and explicitly leaves the value in ecx (checked just below).
- emitGetArg(callee, X86::ecx);
- } else {
- // this sets up the first arg to op_cti_call (func), and explicitly leaves the value in ecx (checked just below).
- emitGetPutArg(callee, 0, X86::ecx);
}
- // Fast check for JS function.
- m_jit.testl_i32r(JSImmediate::TagMask, X86::ecx);
- X86Assembler::JmpSrc isNotObject = m_jit.emitUnlinkedJne();
- m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsFunctionVptr), X86::ecx);
- X86Assembler::JmpSrc isJSFunction = m_jit.emitUnlinkedJe();
- m_jit.link(isNotObject, m_jit.label());
+ // This plants a check for a cached JSFunction value, so we can plant a fast link to the callee.
+ // This deliberately leaves the callee in ecx, used when setting up the stack frame below
+ emitGetArg(callee, X86::ecx);
+ m_jit.cmpl_i32r(reinterpret_cast<unsigned>(JSImmediate::impossibleValue()), X86::ecx);
+ X86Assembler::JmpDst addressOfLinkedFunctionCheck = m_jit.label();
+ m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJne(), i));
+ ASSERT(X86Assembler::getDifferenceBetweenLabels(addressOfLinkedFunctionCheck, m_jit.label()) == repatchOffsetOpCallCall);
+ m_structureStubCompilationInfo[structureIDInstructionIndex].hotPathBegin = addressOfLinkedFunctionCheck;
- // This handles host functions
- emitCTICall(i, ((type == OpConstruct) ? Machine::cti_op_construct_NotJSConstruct : Machine::cti_op_call_NotJSFunction));
+ // The following is the fast case, only used whan a callee can be linked.
- X86Assembler::JmpSrc wasNotJSFunction = m_jit.emitUnlinkedJmp();
- m_jit.link(isJSFunction, m_jit.label());
+ emitGetCTIParam(CTI_ARGS_profilerReference, X86::eax);
+ m_jit.cmpl_i32m(0, X86::eax);
+ X86Assembler::JmpSrc noProfile = m_jit.emitUnlinkedJe();
+ emitPutArg(X86::ecx, 0);
+ emitCTICall(i, Machine::cti_op_call_profiler);
+ emitGetArg(callee, X86::ecx);
+ m_jit.link(noProfile, m_jit.label());
- // This handles JSFunctions
- emitCTICall(i, (type == OpConstruct) ? Machine::cti_op_construct_JSConstruct : Machine::cti_op_call_JSFunction);
+ // In the case of OpConstruct, call oout to a cti_ function to create the new object.
+ if (type == OpConstruct) {
+ emitPutArg(X86::ecx, 0);
+ emitGetPutArg(instruction[3].u.operand, 4, X86::eax);
+ emitCTICall(i, Machine::cti_op_construct_JSConstructFast);
+ emitPutResult(instruction[4].u.operand);
+ emitGetArg(callee, X86::ecx);
+ }
- compileOpCallInitializeCallFrame(callee, argCount);
+ // Fast version of stack frame initialization, directly relative to edi.
+ // Note that this omits to set up RegisterFile::CodeBlock, which is set in the callee
+ m_jit.movl_i32m(reinterpret_cast<unsigned>(nullJSValue), (registerOffset + RegisterFile::OptionalCalleeArguments) * static_cast<int>(sizeof(Register)), X86::edi);
+ m_jit.movl_rm(X86::ecx, (registerOffset + RegisterFile::Callee) * static_cast<int>(sizeof(Register)), X86::edi);
+ m_jit.movl_mr(OBJECT_OFFSET(JSFunction, m_scopeChain) + OBJECT_OFFSET(ScopeChain, m_node), X86::ecx, X86::edx); // newScopeChain
+ m_jit.movl_i32m(argCount, (registerOffset + RegisterFile::ArgumentCount) * static_cast<int>(sizeof(Register)), X86::edi);
+ m_jit.movl_rm(X86::edi, (registerOffset + RegisterFile::CallerFrame) * static_cast<int>(sizeof(Register)), X86::edi);
+ m_jit.movl_rm(X86::edx, (registerOffset + RegisterFile::ScopeChain) * static_cast<int>(sizeof(Register)), X86::edi);
+ m_jit.addl_i32r(registerOffset * sizeof(Register), X86::edi);
- // load ctiCode from the new codeBlock.
- m_jit.movl_mr(OBJECT_OFFSET(CodeBlock, ctiCode), X86::eax, X86::eax);
-
- // Put the new value of 'callFrame' into edi and onto the stack, too.
- m_jit.movl_rr(X86::edx, X86::edi);
-
- // Check the ctiCode has been generated - if not, this is handled in a slow case.
- m_jit.testl_rr(X86::eax, X86::eax);
- m_slowCases.append(SlowCaseEntry(m_jit.emitUnlinkedJe(), i));
- emitCall(i, X86::eax);
+ // Call to the callee
+ m_structureStubCompilationInfo[structureIDInstructionIndex].hotPathOther = emitNakedCall(i, unreachable);
- X86Assembler::JmpDst end = m_jit.label();
- m_jit.link(wasNotJSFunction, end);
if (type == OpCallEval)
- m_jit.link(wasEval, end);
+ m_jit.link(wasEval, m_jit.label());
// Put the return value in dst. In the interpreter, op_ret does this.
emitPutResult(dst);
@@ -1214,7 +1243,7 @@
break;
}
case op_call: {
- compileOpCall(instruction, i);
+ compileOpCall(instruction + i, i, structureIDInstructionIndex++);
i += 7;
break;
}
@@ -1321,7 +1350,7 @@
break;
}
case op_construct: {
- compileOpCall(instruction, i, OpConstruct);
+ compileOpCall(instruction + i, i, structureIDInstructionIndex++, OpConstruct);
i += 7;
break;
}
@@ -1754,14 +1783,15 @@
break;
}
case op_call_eval: {
- compileOpCall(instruction, i, OpCallEval);
+ compileOpCall(instruction + i, i, structureIDInstructionIndex++, OpCallEval);
i += 7;
break;
}
case op_throw: {
emitGetPutArg(instruction[i + 1].u.operand, 0, X86::ecx);
emitCTICall(i, Machine::cti_op_throw);
- m_jit.addl_i8r(0x24, X86::esp);
+ m_jit.addl_i8r(0x20, X86::esp);
+ m_jit.popl_r(X86::ebx);
m_jit.popl_r(X86::edi);
m_jit.popl_r(X86::esi);
m_jit.ret();
@@ -2112,7 +2142,7 @@
Instruction* instruction = m_codeBlock->instructions.begin();
for (Vector<SlowCaseEntry>::iterator iter = m_slowCases.begin(); iter != m_slowCases.end(); ++iter) {
unsigned i = iter->to;
- switch (m_machine->getOpcodeID(instruction[i].u.opcode)) {
+ switch (OpcodeID opcodeID = m_machine->getOpcodeID(instruction[i].u.opcode)) {
case op_convert_this: {
m_jit.link(iter->from, m_jit.label());
m_jit.link((++iter)->from, m_jit.label());
@@ -2577,16 +2607,97 @@
case op_call:
case op_call_eval:
case op_construct: {
+ int dst = instruction[i + 1].u.operand;
+ int callee = instruction[i + 2].u.operand;
+ int argCount = instruction[i + 5].u.operand;
+
m_jit.link(iter->from, m_jit.label());
- // We jump to this slow case if the ctiCode for the codeBlock has not yet been generated; compile it now.
- emitCTICall(i, Machine::cti_vm_compile);
- emitCall(i, X86::eax);
+ // The arguments have been set up on the hot path for op_call_eval
+ if (opcodeID != op_call_eval)
+ compileOpCallSetupArgs(instruction + i, (opcodeID == op_construct), false);
- // Instead of checking for 0 we could initialize the CodeBlock::ctiCode to point to a trampoline that would trigger the translation.
+ // Fast check for JS function.
+ m_jit.testl_i32r(JSImmediate::TagMask, X86::ecx);
+ X86Assembler::JmpSrc callLinkFailNotObject = m_jit.emitUnlinkedJne();
+ m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsFunctionVptr), X86::ecx);
+ X86Assembler::JmpSrc callLinkFailNotJSFunction = m_jit.emitUnlinkedJne();
+
+ // This handles JSFunctions
+ emitGetCTIParam(CTI_ARGS_profilerReference, X86::eax);
+ m_jit.cmpl_i32m(0, X86::eax);
+ X86Assembler::JmpSrc noProfile1 = m_jit.emitUnlinkedJe();
+ emitCTICall(i, Machine::cti_op_call_profiler);
+ m_jit.link(noProfile1, m_jit.label());
+
+ emitCTICall(i, (opcodeID == op_construct) ? Machine::cti_op_construct_JSConstruct : Machine::cti_op_call_JSFunction);
+ // initialize the new call frame (pointed to by edx, after the last call), then set edi to point to it.
+ compileOpCallInitializeCallFrame(callee, argCount);
+ m_jit.movl_rr(X86::edx, X86::edi);
+
+ // Try to link & repatch this call.
+ m_structureStubCompilationInfo[structureIDInstructionIndex].callReturnLocation =
+ emitCTICall(i, Machine::cti_vm_lazyLinkCall);
+ emitNakedCall(i, X86::eax);
+ X86Assembler::JmpSrc storeResultForFirstRun = m_jit.emitUnlinkedJmp();
+
+ // This is the address for the cold path *after* the first run (which tries to link the call).
+ m_structureStubCompilationInfo[structureIDInstructionIndex].coldPathOther = m_jit.label();
+
+ // The arguments have been set up on the hot path for op_call_eval
+ if (opcodeID != op_call_eval)
+ compileOpCallSetupArgs(instruction + i, (opcodeID == op_construct), false);
+
+ // Check for JSFunctions.
+ m_jit.testl_i32r(JSImmediate::TagMask, X86::ecx);
+ X86Assembler::JmpSrc isNotObject = m_jit.emitUnlinkedJne();
+ m_jit.cmpl_i32m(reinterpret_cast<unsigned>(m_machine->m_jsFunctionVptr), X86::ecx);
+ X86Assembler::JmpSrc isJSFunction = m_jit.emitUnlinkedJe();
+
+ // This handles host functions
+ X86Assembler::JmpDst notJSFunctionlabel = m_jit.label();
+ m_jit.link(isNotObject, notJSFunctionlabel);
+ m_jit.link(callLinkFailNotObject, notJSFunctionlabel);
+ m_jit.link(callLinkFailNotJSFunction, notJSFunctionlabel);
+ emitCTICall(i, ((opcodeID == op_construct) ? Machine::cti_op_construct_NotJSConstruct : Machine::cti_op_call_NotJSFunction));
+ X86Assembler::JmpSrc wasNotJSFunction = m_jit.emitUnlinkedJmp();
+
+ // Next, handle JSFunctions...
+ m_jit.link(isJSFunction, m_jit.label());
+
+ // Profiler check
+ emitGetCTIParam(CTI_ARGS_profilerReference, X86::eax);
+ m_jit.cmpl_i32m(0, X86::eax);
+ X86Assembler::JmpSrc noProfile = m_jit.emitUnlinkedJe();
+ emitCTICall(i, Machine::cti_op_call_profiler);
+ m_jit.link(noProfile, m_jit.label());
+
+ emitCTICall(i, (opcodeID == op_construct) ? Machine::cti_op_construct_JSConstruct : Machine::cti_op_call_JSFunction);
+ // initialize the new call frame (pointed to by edx, after the last call).
+ compileOpCallInitializeCallFrame(callee, argCount);
+ m_jit.movl_rr(X86::edx, X86::edi);
+
+ // load ctiCode from the new codeBlock.
+ m_jit.movl_mr(OBJECT_OFFSET(CodeBlock, ctiCode), X86::eax, X86::eax);
+
+ // Move the new callframe into edi.
+ m_jit.movl_rr(X86::edx, X86::edi);
+
+ // Check the ctiCode has been generated (if not compile it now), and make the call.
+ m_jit.testl_rr(X86::eax, X86::eax);
+ X86Assembler::JmpSrc hasCode = m_jit.emitUnlinkedJne();
+ emitCTICall(i, Machine::cti_vm_compile);
+ m_jit.link(hasCode, m_jit.label());
+
+ emitNakedCall(i, X86::eax);
// Put the return value in dst. In the interpreter, op_ret does this.
- emitPutResult(instruction[i + 1].u.operand);
+ X86Assembler::JmpDst storeResult = m_jit.label();
+ m_jit.link(wasNotJSFunction, storeResult);
+ m_jit.link(storeResultForFirstRun, storeResult);
+ emitPutResult(dst);
+
+ ++structureIDInstructionIndex;
i += 7;
break;
}
@@ -2622,6 +2733,9 @@
X86Assembler::JmpSrc slowRegisterFileCheck;
X86Assembler::JmpDst afterRegisterFileCheck;
if (m_codeBlock->codeType == FunctionCode) {
+ // In the case of a fast linked call, we do not set this up in the caller.
+ m_jit.movl_i32m(reinterpret_cast<unsigned>(m_codeBlock), RegisterFile::CodeBlock * static_cast<int>(sizeof(Register)), X86::edi);
+
emitGetCTIParam(CTI_ARGS_registerFile, X86::eax);
m_jit.leal_mr(m_codeBlock->numCalleeRegisters * sizeof(Register), X86::edi, X86::edx);
m_jit.cmpl_mr(OBJECT_OFFSET(RegisterFile, m_end), X86::eax, X86::edx);
@@ -2690,6 +2804,8 @@
StructureStubInfo& info = m_codeBlock->structureIDInstructions[i];
info.callReturnLocation = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].callReturnLocation);
info.hotPathBegin = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].hotPathBegin);
+ info.hotPathOther = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].hotPathOther);
+ info.coldPathOther = X86Assembler::getRelocatedAddress(code, m_structureStubCompilationInfo[i].coldPathOther);
}
m_codeBlock->ctiCode = code;
@@ -2972,6 +3088,33 @@
ctiRepatchCallByReturnAddress(returnAddress, code);
}
+void CTI::unlinkCall(StructureStubInfo* structureStubInfo)
+{
+ // When the JSFunction is deleted the pointer embedded in the instruction stream will no longer be valid
+ // (and, if a new JSFunction happened to be constructed at the same location, we could get a false positive
+ // match). Reset the check so it no longer matches.
+ reinterpret_cast<void**>(structureStubInfo->hotPathBegin)[-1] = JSImmediate::impossibleValue();
+}
+
+void CTI::linkCall(CodeBlock* callerCodeBlock, JSFunction* callee, CodeBlock* calleeCodeBlock, void* ctiCode, void* returnAddress, int callerArgCount)
+{
+ StructureStubInfo& stubInfo = callerCodeBlock->getStubInfo(returnAddress);
+
+ // Currently we only link calls with the exact number of arguments.
+ if (callerArgCount == calleeCodeBlock->numParameters) {
+ ASSERT(!stubInfo.linkInfoPtr);
+
+ calleeCodeBlock->addCaller(&stubInfo);
+
+ reinterpret_cast<void**>(stubInfo.hotPathBegin)[-1] = callee;
+ ctiRepatchCallByReturnAddress(stubInfo.hotPathOther, ctiCode);
+ }
+
+ // repatch the instruction that jumps out to the cold path, so that we only try to link once.
+ void* repatchCheck = reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(stubInfo.hotPathBegin) + repatchOffsetOpCallCall);
+ ctiRepatchCallByReturnAddress(repatchCheck, stubInfo.coldPathOther);
+}
+
void* CTI::privateCompileArrayLengthTrampoline()
{
// Check eax is an array
diff --git a/JavaScriptCore/VM/CTI.h b/JavaScriptCore/VM/CTI.h
index 26338dc..0efd9de 100644
--- a/JavaScriptCore/VM/CTI.h
+++ b/JavaScriptCore/VM/CTI.h
@@ -66,6 +66,7 @@
#define ARG_int3 ((int)((ARGS)[3]))
#define ARG_int4 ((int)((ARGS)[4]))
#define ARG_int5 ((int)((ARGS)[5]))
+#define ARG_int6 ((int)((ARGS)[6]))
#define ARG_func1 ((FuncDeclNode*)((ARGS)[1]))
#define ARG_funcexp1 ((FuncExprNode*)((ARGS)[1]))
#define ARG_registers1 ((Register*)((ARGS)[1]))
@@ -94,6 +95,7 @@
class StructureIDChain;
struct Instruction;
struct OperandTypes;
+ struct StructureStubInfo;
typedef JSValue* (SFX_CALL *CTIHelper_j)(CTI_ARGS);
typedef JSPropertyNameIterator* (SFX_CALL *CTIHelper_p)(CTI_ARGS);
@@ -222,6 +224,8 @@
struct StructureStubCompilationInfo {
X86Assembler::JmpSrc callReturnLocation;
X86Assembler::JmpDst hotPathBegin;
+ X86Assembler::JmpSrc hotPathOther;
+ X86Assembler::JmpDst coldPathOther;
};
extern "C" {
@@ -257,6 +261,7 @@
#else
static const int repatchOffsetGetByIdSlowCaseCall = 17 + 4 + ctiArgumentInitSize;
#endif
+ static const int repatchOffsetOpCallCall = 6;
public:
static void compile(Machine* machine, CallFrame* callFrame, CodeBlock* codeBlock)
@@ -320,6 +325,9 @@
return cti.privateCompilePatchGetArrayLength(returnAddress);
}
+ static void linkCall(CodeBlock* callerCodeBlock, JSFunction* callee, CodeBlock* calleeCodeBlock, void* ctiCode, void* returnAddress, int callerArgCount);
+ static void unlinkCall(StructureStubInfo*);
+
inline static JSValue* execute(void* code, RegisterFile* registerFile, CallFrame* callFrame, JSGlobalData* globalData, JSValue** exception)
{
return ctiTrampoline(code, registerFile, callFrame, exception, Profiler::enabledProfilerReference(), globalData);
@@ -346,8 +354,9 @@
void privateCompilePatchGetArrayLength(void* returnAddress);
enum CompileOpCallType { OpCallNormal, OpCallEval, OpConstruct };
- void compileOpCall(Instruction* instruction, unsigned i, CompileOpCallType type = OpCallNormal);
+ void compileOpCall(Instruction* instruction, unsigned i, unsigned structureIDInstructionIndex, CompileOpCallType type = OpCallNormal);
void compileOpCallInitializeCallFrame(unsigned callee, unsigned argCount);
+ void compileOpCallSetupArgs(Instruction* instruction, bool isConstruct, bool isEval);
enum CompileOpStrictEqType { OpStrictEq, OpNStrictEq };
void compileOpStrictEq(Instruction* instruction, unsigned i, CompileOpStrictEqType type);
void putDoubleResultToJSNumberCellOrJSImmediate(X86::XMMRegisterID xmmSource, X86::RegisterID jsNumberCell, unsigned dst, X86Assembler::JmpSrc* wroteJSNumberCell, X86::XMMRegisterID tempXmm, X86::RegisterID tempReg1, X86::RegisterID tempReg2);
@@ -388,7 +397,8 @@
void emitTagAsBoolImmediate(X86Assembler::RegisterID reg);
- X86Assembler::JmpSrc emitCall(unsigned opcodeIndex, X86::RegisterID);
+ X86Assembler::JmpSrc emitNakedCall(unsigned opcodeIndex, X86::RegisterID);
+ X86Assembler::JmpSrc emitNakedCall(unsigned opcodeIndex, void(*function)());
X86Assembler::JmpSrc emitCTICall(unsigned opcodeIndex, CTIHelper_j);
X86Assembler::JmpSrc emitCTICall(unsigned opcodeIndex, CTIHelper_p);
X86Assembler::JmpSrc emitCTICall(unsigned opcodeIndex, CTIHelper_v);
diff --git a/JavaScriptCore/VM/CodeBlock.cpp b/JavaScriptCore/VM/CodeBlock.cpp
index d28fcc7..3dce54b 100644
--- a/JavaScriptCore/VM/CodeBlock.cpp
+++ b/JavaScriptCore/VM/CodeBlock.cpp
@@ -30,6 +30,7 @@
#include "config.h"
#include "CodeBlock.h"
+#include "CTI.h"
#include "JSValue.h"
#include "Machine.h"
#include "debugger.h"
@@ -931,13 +932,34 @@
derefStructureIDs(&instructions[structureIDInstructions[i].opcodeIndex]);
if (structureIDInstructions[i].stubRoutine)
fastFree(structureIDInstructions[i].stubRoutine);
+ if (CallLinkInfo* callLinkInfo = structureIDInstructions[i].linkInfoPtr) {
+ callLinkInfo->callee->removeCaller(callLinkInfo);
+ delete callLinkInfo;
+ }
}
-#if ENABLE(CTI)
+
+#if ENABLE(CTI)
+ unlinkCallers();
+
if (ctiCode)
fastFree(ctiCode);
#endif
}
+#if ENABLE(CTI)
+void CodeBlock::unlinkCallers()
+{
+ size_t size = linkedCallerList.size();
+ for (size_t i = 0; i < size; ++i) {
+ CallLinkInfo* currentCaller = linkedCallerList[i];
+ CTI::unlinkCall(currentCaller->callerStructureStubInfo);
+ currentCaller->callerStructureStubInfo->linkInfoPtr = 0;
+ delete currentCaller;
+ }
+ linkedCallerList.clear();
+}
+#endif
+
void CodeBlock::derefStructureIDs(Instruction* vPC) const
{
Machine* machine = globalData->machine;
@@ -973,7 +995,8 @@
}
// These instructions don't ref their StructureIDs.
- ASSERT(vPC[0].u.opcode == machine->getOpcode(op_get_by_id) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id) || vPC[0].u.opcode == machine->getOpcode(op_get_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_get_array_length) || vPC[0].u.opcode == machine->getOpcode(op_get_string_length));
+ ASSERT(vPC[0].u.opcode == machine->getOpcode(op_get_by_id) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id) || vPC[0].u.opcode == machine->getOpcode(op_get_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_get_array_length) || vPC[0].u.opcode == machine->getOpcode(op_get_string_length)
+ || vPC[0].u.opcode == machine->getOpcode(op_call_eval) || vPC[0].u.opcode == machine->getOpcode(op_call) || vPC[0].u.opcode == machine->getOpcode(op_construct));
}
void CodeBlock::refStructureIDs(Instruction* vPC) const
diff --git a/JavaScriptCore/VM/CodeBlock.h b/JavaScriptCore/VM/CodeBlock.h
index bf17356..6084db1 100644
--- a/JavaScriptCore/VM/CodeBlock.h
+++ b/JavaScriptCore/VM/CodeBlock.h
@@ -78,12 +78,17 @@
#endif
};
+ struct CallLinkInfo;
+
struct StructureStubInfo {
StructureStubInfo(unsigned opcodeIndex)
: opcodeIndex(opcodeIndex)
, stubRoutine(0)
, callReturnLocation(0)
, hotPathBegin(0)
+ , hotPathOther(0)
+ , coldPathOther(0)
+ , linkInfoPtr(0)
{
}
@@ -91,6 +96,21 @@
void* stubRoutine;
void* callReturnLocation;
void* hotPathBegin;
+ void* hotPathOther;
+ void* coldPathOther;
+ CallLinkInfo* linkInfoPtr;
+ };
+
+ struct CallLinkInfo {
+ CodeBlock* callee;
+ StructureStubInfo* callerStructureStubInfo;
+ unsigned position;
+
+ CallLinkInfo(CodeBlock* c, StructureStubInfo* css)
+ {
+ callee = c;
+ callerStructureStubInfo = css;
+ }
};
struct StringJumpTable {
@@ -203,6 +223,30 @@
~CodeBlock();
+#if ENABLE(CTI)
+ void unlinkCallers();
+#endif
+
+ void addCaller(StructureStubInfo* caller)
+ {
+ CallLinkInfo* callLinkInfo = new CallLinkInfo(this, caller);
+ caller->linkInfoPtr = callLinkInfo;
+ callLinkInfo->position = linkedCallerList.size();
+ linkedCallerList.append(callLinkInfo);
+ }
+
+ void removeCaller(CallLinkInfo* caller)
+ {
+ unsigned pos = caller->position;
+ unsigned lastPos = linkedCallerList.size() - 1;
+
+ if (pos != lastPos) {
+ linkedCallerList[pos] = linkedCallerList[lastPos];
+ linkedCallerList[pos]->position = pos;
+ }
+ linkedCallerList.shrink(lastPos);
+ }
+
#if !defined(NDEBUG) || ENABLE_SAMPLING_TOOL
void dump(ExecState*) const;
void printStructureIDs(const Instruction*) const;
@@ -221,6 +265,7 @@
{
// FIXME: would a binary chop be faster here?
for (unsigned i = 0; ; ++i) {
+ ASSERT(i < structureIDInstructions.size());
if (structureIDInstructions[i].callReturnLocation == returnAddress)
return structureIDInstructions[i];
}
@@ -251,6 +296,7 @@
Vector<Instruction> instructions;
Vector<StructureStubInfo> structureIDInstructions;
+ Vector<CallLinkInfo*> linkedCallerList;
// Constant pool
Vector<Identifier> identifiers;
diff --git a/JavaScriptCore/VM/CodeGenerator.cpp b/JavaScriptCore/VM/CodeGenerator.cpp
index 4cfa7d7..4e9e5ae 100644
--- a/JavaScriptCore/VM/CodeGenerator.cpp
+++ b/JavaScriptCore/VM/CodeGenerator.cpp
@@ -1153,6 +1153,7 @@
callFrame.append(newTemporary());
emitExpressionInfo(divot, startOffset, endOffset);
+ m_codeBlock->structureIDInstructions.append(instructions().size());
emitOpcode(opcodeID);
instructions().append(dst->index());
instructions().append(func->index());
@@ -1204,6 +1205,7 @@
callFrame.append(newTemporary());
emitExpressionInfo(divot, startOffset, endOffset);
+ m_codeBlock->structureIDInstructions.append(instructions().size());
emitOpcode(op_construct);
instructions().append(dst->index());
instructions().append(func->index());
diff --git a/JavaScriptCore/VM/Machine.cpp b/JavaScriptCore/VM/Machine.cpp
index cf6d8d7..ef36e03 100644
--- a/JavaScriptCore/VM/Machine.cpp
+++ b/JavaScriptCore/VM/Machine.cpp
@@ -4544,6 +4544,12 @@
return ARG_func1->makeFunction(ARG_callFrame, ARG_callFrame->scopeChain());
}
+void Machine::cti_op_call_profiler(CTI_ARGS)
+{
+ ASSERT(*ARG_profilerReference);
+ (*ARG_profilerReference)->willExecute(ARG_callFrame, static_cast<JSFunction*>(ARG_src1));
+}
+
VoidPtrPair Machine::cti_op_call_JSFunction(CTI_ARGS)
{
#ifndef NDEBUG
@@ -4551,9 +4557,6 @@
ASSERT(ARG_src1->getCallData(callData) == CallTypeJS);
#endif
- if (UNLIKELY(*ARG_profilerReference != 0))
- (*ARG_profilerReference)->willExecute(ARG_callFrame, static_cast<JSFunction*>(ARG_src1));
-
ScopeChainNode* callDataScopeChain = static_cast<JSFunction*>(ARG_src1)->m_scopeChain.node();
CodeBlock* newCodeBlock = &static_cast<JSFunction*>(ARG_src1)->m_body->byteCode(callDataScopeChain);
CallFrame* callFrame = ARG_callFrame;
@@ -4593,6 +4596,24 @@
return pair;
}
+void* Machine::cti_vm_lazyLinkCall(CTI_ARGS)
+{
+ Machine* machine = ARG_globalData->machine;
+ CallFrame* callFrame = CallFrame::create(ARG_callFrame);
+ CallFrame* callerCallFrame = callFrame->callerFrame();
+ CodeBlock* callerCodeBlock = callerCallFrame->codeBlock();
+
+ JSFunction* callee = static_cast<JSFunction*>(ARG_src1);
+ CodeBlock* codeBlock = &callee->m_body->byteCode(callee->m_scopeChain.node());
+ if (!codeBlock->ctiCode)
+ CTI::compile(machine, callFrame, codeBlock);
+
+ int argCount = ARG_int3;
+ CTI::linkCall(callerCodeBlock, callee, codeBlock, codeBlock->ctiCode, CTI_RETURN_ADDRESS, argCount);
+
+ return codeBlock->ctiCode;
+}
+
void* Machine::cti_vm_compile(CTI_ARGS)
{
CodeBlock* codeBlock = ARG_callFrame->codeBlock();
@@ -4715,24 +4736,36 @@
VM_THROW_EXCEPTION();
}
+JSValue* Machine::cti_op_construct_JSConstructFast(CTI_ARGS)
+{
+#ifndef NDEBUG
+ ConstructData constructData;
+ ASSERT(static_cast<JSFunction*>(ARG_src1)->getConstructData(constructData) == ConstructTypeJS);
+#endif
+
+ StructureID* structure;
+ if (ARG_src2->isObject())
+ structure = static_cast<JSObject*>(ARG_src2)->inheritorID();
+ else
+ structure = static_cast<JSFunction*>(ARG_src1)->m_scopeChain.node()->globalObject()->emptyObjectStructure();
+ return new (ARG_globalData) JSObject(structure);
+}
+
VoidPtrPair Machine::cti_op_construct_JSConstruct(CTI_ARGS)
{
CallFrame* callFrame = ARG_callFrame;
JSFunction* constructor = static_cast<JSFunction*>(ARG_src1);
- JSValue* constrProtoVal = ARG_src2;
- int firstArg = ARG_int3;
- int registerOffset = ARG_int4;
- int argCount = ARG_int5;
+ int registerOffset = ARG_int2;
+ int argCount = ARG_int3;
+ JSValue* constrProtoVal = ARG_src5;
+ int firstArg = ARG_int6;
#ifndef NDEBUG
ConstructData constructData;
- ASSERT(ARG_src1->getConstructData(constructData) == ConstructTypeJS);
+ ASSERT(constructor->getConstructData(constructData) == ConstructTypeJS);
#endif
- if (*ARG_profilerReference)
- (*ARG_profilerReference)->willExecute(callFrame, constructor);
-
ScopeChainNode* callDataScopeChain = constructor->m_scopeChain.node();
FunctionBodyNode* functionBodyNode = constructor->m_body.get();
CodeBlock* newCodeBlock = &functionBodyNode->byteCode(callDataScopeChain);
@@ -4783,8 +4816,8 @@
CallFrame* callFrame = ARG_callFrame;
JSValue* constrVal = ARG_src1;
- int firstArg = ARG_int3;
- int argCount = ARG_int5;
+ int argCount = ARG_int3;
+ int firstArg = ARG_int6;
ConstructData constructData;
ConstructType constructType = constrVal->getConstructData(constructData);
@@ -4810,7 +4843,7 @@
ASSERT(constructType == ConstructTypeNone);
- ARG_globalData->exception = createNotAConstructorError(callFrame, constrVal, ARG_instr6, callFrame->codeBlock());
+ ARG_globalData->exception = createNotAConstructorError(callFrame, constrVal, ARG_instr4, callFrame->codeBlock());
VM_THROW_EXCEPTION();
}
diff --git a/JavaScriptCore/VM/Machine.h b/JavaScriptCore/VM/Machine.h
index 73bdb88..682a5de 100644
--- a/JavaScriptCore/VM/Machine.h
+++ b/JavaScriptCore/VM/Machine.h
@@ -188,11 +188,11 @@
static void SFX_CALL cti_op_create_arguments(CTI_ARGS);
static void SFX_CALL cti_op_tear_off_activation(CTI_ARGS);
static void SFX_CALL cti_op_tear_off_arguments(CTI_ARGS);
- static void SFX_CALL cti_op_ret_profiler(CTI_ARGS);
static void SFX_CALL cti_op_ret_scopeChain(CTI_ARGS);
static JSValue* SFX_CALL cti_op_new_array(CTI_ARGS);
static JSValue* SFX_CALL cti_op_resolve(CTI_ARGS);
static JSValue* SFX_CALL cti_op_resolve_global(CTI_ARGS);
+ static JSValue* SFX_CALL cti_op_construct_JSConstructFast(CTI_ARGS);
static VoidPtrPair SFX_CALL cti_op_construct_JSConstruct(CTI_ARGS);
static JSValue* SFX_CALL cti_op_construct_NotJSConstruct(CTI_ARGS);
static JSValue* SFX_CALL cti_op_get_by_val(CTI_ARGS);
@@ -255,8 +255,12 @@
static JSValue* SFX_CALL cti_op_new_error(CTI_ARGS);
static void SFX_CALL cti_op_debug(CTI_ARGS);
+ static void SFX_CALL cti_op_call_profiler(CTI_ARGS);
+ static void SFX_CALL cti_op_ret_profiler(CTI_ARGS);
+
static void* SFX_CALL cti_vm_throw(CTI_ARGS);
static void* SFX_CALL cti_vm_compile(CTI_ARGS);
+ static void* SFX_CALL cti_vm_lazyLinkCall(CTI_ARGS);
static JSValue* SFX_CALL cti_op_push_activation(CTI_ARGS);
#endif // ENABLE(CTI)
diff --git a/JavaScriptCore/kjs/JSFunction.cpp b/JavaScriptCore/kjs/JSFunction.cpp
index a70d293..d54f160 100644
--- a/JavaScriptCore/kjs/JSFunction.cpp
+++ b/JavaScriptCore/kjs/JSFunction.cpp
@@ -25,6 +25,7 @@
#include "config.h"
#include "JSFunction.h"
+#include "CodeBlock.h"
#include "CommonIdentifiers.h"
#include "ExecState.h"
#include "FunctionPrototype.h"
@@ -51,6 +52,17 @@
{
}
+JSFunction::~JSFunction()
+{
+#if ENABLE(CTI)
+ // JIT code for other functions may have had calls linked directly to the code for this function; these links
+ // are based on a check for the this pointer value for this JSFunction - which will no longer be valid once
+ // this memory is freed and may be reused (potentially for another, different JSFunction).
+ if (m_body.get() && m_body->isGenerated())
+ m_body->generatedByteCode().unlinkCallers();
+#endif
+}
+
void JSFunction::mark()
{
Base::mark();
diff --git a/JavaScriptCore/kjs/JSFunction.h b/JavaScriptCore/kjs/JSFunction.h
index 241c908..aef022d 100644
--- a/JavaScriptCore/kjs/JSFunction.h
+++ b/JavaScriptCore/kjs/JSFunction.h
@@ -45,6 +45,7 @@
JSFunction(PassRefPtr<JSC::StructureID> st) : InternalFunction(st), m_scopeChain(NoScopeChain()) {}
public:
JSFunction(ExecState*, const Identifier&, FunctionBodyNode*, ScopeChainNode*);
+ ~JSFunction();
virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
virtual void put(ExecState*, const Identifier& propertyName, JSValue*, PutPropertySlot&);
diff --git a/JavaScriptCore/kjs/nodes.h b/JavaScriptCore/kjs/nodes.h
index 61e189e..03e4e54 100644
--- a/JavaScriptCore/kjs/nodes.h
+++ b/JavaScriptCore/kjs/nodes.h
@@ -2278,6 +2278,11 @@
return *m_code;
}
+ bool isGenerated() JSC_FAST_CALL
+ {
+ return m_code;
+ }
+
void mark();
void finishParsing(const SourceCode&, ParameterNode*);
diff --git a/JavaScriptCore/masm/X86Assembler.h b/JavaScriptCore/masm/X86Assembler.h
index dd08d38..406d723 100644
--- a/JavaScriptCore/masm/X86Assembler.h
+++ b/JavaScriptCore/masm/X86Assembler.h
@@ -1057,6 +1057,11 @@
return dst.m_offset - src.m_offset;
}
+ static int getDifferenceBetweenLabels(JmpSrc src, JmpDst dst)
+ {
+ return dst.m_offset - src.m_offset;
+ }
+
static void repatchImmediate(intptr_t where, int32_t value)
{
reinterpret_cast<int32_t*>(where)[-1] = value;