Commit be8ad1fd authored by ggaren@apple.com's avatar ggaren@apple.com
Browse files

Optimized closures that capture arguments

https://bugs.webkit.org/show_bug.cgi?id=97358

Reviewed by Oliver Hunt.

Source/JavaScriptCore: 

Previously, the activation object was responsible for capturing all
arguments in a way that was convenient for the arguments object. Now,
we move all captured variables into a contiguous region in the stack,
allocate an activation for exactly that size, and make the arguments
object responsible for knowing all the places to which arguments could
have moved.

This seems like the right tradeoff because

    (a) Closures are common and long-lived, so we want them to be small.

    (b) Our primary strategy for optimizing the arguments object is to make
    it go away. If you're allocating arguments objects, you're already having
    a bad time.

    (c) It's common to use either the arguments object or named argument
    closure, but not both.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
(JSC::CodeBlock::CodeBlock):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::argumentsRegister):
(JSC::CodeBlock::activationRegister):
(JSC::CodeBlock::isCaptured):
(JSC::CodeBlock::argumentIndexAfterCapture): m_numCapturedVars is gone
now -- we have an explicit range instead.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator): Move captured arguments
into the captured region of local variables for space efficiency. Record
precise data about where they moved for the sake of the arguments object.

Some of this data was previously wrong, but it didn't cause any problems
because the arguments weren't actually moving.

* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::flushArgumentsAndCapturedVariables): Don't
assume that captured vars are in any particular location -- always ask
the CodeBlock. This is better encapsulation.

(JSC::DFG::ByteCodeParser::parseCodeBlock):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile): I rename things sometimes.

* runtime/Arguments.cpp:
(JSC::Arguments::tearOff): Account for a particularly nasty edge case.

(JSC::Arguments::didTearOffActivation): Don't allocate our slow arguments
data on tear-off. We need to allocate it eagerly instead, since we need
to know about displaced, captured arguments during access before tear-off.

* runtime/Arguments.h:
(JSC::Arguments::allocateSlowArguments):
(JSC::Arguments::argument): Tell our slow arguments array where all arguments
are, even if they are not captured. This simplifies some things, so we don't
have to account explicitly for the full matrix of (not torn off, torn off)
* (captured, not captured).

(JSC::Arguments::finishCreation): Allocate our slow arguments array eagerly
because we need to know about displaced, captured arguments during access
before tear-off.

* runtime/Executable.cpp:
(JSC::FunctionExecutable::FunctionExecutable):
(JSC::FunctionExecutable::compileForCallInternal):
(JSC::FunctionExecutable::compileForConstructInternal):
* runtime/Executable.h:
(JSC::FunctionExecutable::parameterCount):
(FunctionExecutable):
* runtime/JSActivation.cpp:
(JSC::JSActivation::visitChildren):
* runtime/JSActivation.h:
(JSActivation):
(JSC::JSActivation::create):
(JSC::JSActivation::JSActivation):
(JSC::JSActivation::registerOffset):
(JSC::JSActivation::tearOff):
(JSC::JSActivation::allocationSize):
(JSC::JSActivation::isValid): This is really the point of the patch. All
the pointer math in Activations basically boils away, since we always
copy a contiguous region of captured variables now.

* runtime/SymbolTable.h:
(JSC::SlowArgument::SlowArgument):
(SlowArgument):
(SharedSymbolTable):
(JSC::SharedSymbolTable::captureCount):
(JSC::SharedSymbolTable::SharedSymbolTable): AllOfTheThings capture mode
is gone now -- that's the point of the patch. indexIfCaptured gets renamed
to index because we always have an index, even if not captured. (The only
time when the index is meaningless is when we're Deleted.)

LayoutTests: 

* fast/js/dfg-arguments-alias-activation-expected.txt:
* fast/js/dfg-arguments-alias-activation.html:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@129297 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 9de834a3
2012-09-21 Geoffrey Garen <ggaren@apple.com>
Optimized closures that capture arguments
https://bugs.webkit.org/show_bug.cgi?id=97358
Reviewed by Oliver Hunt.
* fast/js/dfg-arguments-alias-activation-expected.txt:
* fast/js/dfg-arguments-alias-activation.html:
2012-09-21 Adam Klein <adamk@chromium.org>
 
Remove bogus assertions from ChildListMutationScope
......@@ -21,19 +21,54 @@ if (window.testRunner) {
testRunner.dumpAsText();
}
// In-bounds of declared and passed arguments.
function f(x) {
// In-bounds of declared and passed arguments, no activation, before tear-off.
function f0(x) {
return arguments[0] || function() { return x; };
}
// Out-of-bounds of declared arguments, in-bounds of passed arguments.
function g(x) {
// Out-of-bounds of declared arguments, in-bounds of passed arguments, no activation, before tear-off.
function f1(x) {
return arguments[1] || function() { return x; };
}
for (var i = 0; i < 200; ++i)
shouldBe(f(1), "f(1)", 1);
// In-bounds of declared arguments, in-bounds of passed arguments, yes activation, before tear-off.
function f2(x) {
return function() { return x; } && arguments[0];
}
// Out-of-bounds of declared arguments, in-bounds of passed arguments, yes activation, before tear-off.
function f3(x) {
return function() { return x; } && arguments[1];
}
// In-bounds of declared and passed arguments, no activation, after tear-off.
function f4(x) {
return arguments || function() { return x; };
}
// Out-of-bounds of declared arguments, in-bounds of passed arguments, no activation, after tear-off.
function f5(x) {
return arguments || function() { return x; };
}
// In-bounds of declared arguments, in-bounds of passed arguments, yes activation, after tear-off.
function f6(x) {
return function() { return x; } && arguments;
}
for (var i = 0; i < 200; ++i)
shouldBe(g(0, 1), "g(0, 1)", 1);
// Out-of-bounds of declared arguments, in-bounds of passed arguments, yes activation, after tear-off.
function f7(x) {
return function() { return x; } && arguments;
}
for (var i = 0; i < 200; ++i) {
shouldBe(f0(1), "f0(1)", 1);
shouldBe(f1(2, 3), "f1(2, 3)", 3);
shouldBe(f2(4), "f2(4)", 4);
shouldBe(f3(5, 6), "f3(5, 6)", 6);
shouldBe(f4(7)[0], "f4(7)", 7);
shouldBe(f5(8, 9)[1], "f5(8, 9)", 9);
shouldBe(f6(10)[0], "f6(10)", 10);
shouldBe(f7(11, 12)[1], "f7(11, 12)", 12);
}
</script>
2012-09-21 Geoffrey Garen <ggaren@apple.com>
Optimized closures that capture arguments
https://bugs.webkit.org/show_bug.cgi?id=97358
Reviewed by Oliver Hunt.
Previously, the activation object was responsible for capturing all
arguments in a way that was convenient for the arguments object. Now,
we move all captured variables into a contiguous region in the stack,
allocate an activation for exactly that size, and make the arguments
object responsible for knowing all the places to which arguments could
have moved.
This seems like the right tradeoff because
(a) Closures are common and long-lived, so we want them to be small.
(b) Our primary strategy for optimizing the arguments object is to make
it go away. If you're allocating arguments objects, you're already having
a bad time.
(c) It's common to use either the arguments object or named argument
closure, but not both.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
(JSC::CodeBlock::CodeBlock):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::argumentsRegister):
(JSC::CodeBlock::activationRegister):
(JSC::CodeBlock::isCaptured):
(JSC::CodeBlock::argumentIndexAfterCapture): m_numCapturedVars is gone
now -- we have an explicit range instead.
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator): Move captured arguments
into the captured region of local variables for space efficiency. Record
precise data about where they moved for the sake of the arguments object.
Some of this data was previously wrong, but it didn't cause any problems
because the arguments weren't actually moving.
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::flushArgumentsAndCapturedVariables): Don't
assume that captured vars are in any particular location -- always ask
the CodeBlock. This is better encapsulation.
(JSC::DFG::ByteCodeParser::parseCodeBlock):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile): I rename things sometimes.
* runtime/Arguments.cpp:
(JSC::Arguments::tearOff): Account for a particularly nasty edge case.
(JSC::Arguments::didTearOffActivation): Don't allocate our slow arguments
data on tear-off. We need to allocate it eagerly instead, since we need
to know about displaced, captured arguments during access before tear-off.
* runtime/Arguments.h:
(JSC::Arguments::allocateSlowArguments):
(JSC::Arguments::argument): Tell our slow arguments array where all arguments
are, even if they are not captured. This simplifies some things, so we don't
have to account explicitly for the full matrix of (not torn off, torn off)
* (captured, not captured).
(JSC::Arguments::finishCreation): Allocate our slow arguments array eagerly
because we need to know about displaced, captured arguments during access
before tear-off.
* runtime/Executable.cpp:
(JSC::FunctionExecutable::FunctionExecutable):
(JSC::FunctionExecutable::compileForCallInternal):
(JSC::FunctionExecutable::compileForConstructInternal):
* runtime/Executable.h:
(JSC::FunctionExecutable::parameterCount):
(FunctionExecutable):
* runtime/JSActivation.cpp:
(JSC::JSActivation::visitChildren):
* runtime/JSActivation.h:
(JSActivation):
(JSC::JSActivation::create):
(JSC::JSActivation::JSActivation):
(JSC::JSActivation::registerOffset):
(JSC::JSActivation::tearOff):
(JSC::JSActivation::allocationSize):
(JSC::JSActivation::isValid): This is really the point of the patch. All
the pointer math in Activations basically boils away, since we always
copy a contiguous region of captured variables now.
* runtime/SymbolTable.h:
(JSC::SlowArgument::SlowArgument):
(SlowArgument):
(SharedSymbolTable):
(JSC::SharedSymbolTable::captureCount):
(JSC::SharedSymbolTable::SharedSymbolTable): AllOfTheThings capture mode
is gone now -- that's the point of the patch. indexIfCaptured gets renamed
to index because we always have an index, even if not captured. (The only
time when the index is meaningless is when we're Deleted.)
2012-09-21 Gavin Barraclough <barraclough@apple.com>
 
Eeeep - broke early boyer in bug#97382
......
......@@ -532,8 +532,8 @@ void CodeBlock::dump(ExecState* exec)
static_cast<unsigned long>(instructions().size() * sizeof(Instruction)),
this, codeTypeToString(codeType()), m_numParameters, m_numCalleeRegisters,
m_numVars);
if (m_numCapturedVars)
dataLog("; %d captured var(s)", m_numCapturedVars);
if (m_symbolTable->captureCount())
dataLog("; %d captured var(s)", m_symbolTable->captureCount());
if (usesArguments()) {
dataLog(
"; uses arguments, in r%d, r%d",
......@@ -1709,7 +1709,6 @@ CodeBlock::CodeBlock(CopyParsedBlockTag, CodeBlock& other)
, m_heap(other.m_heap)
, m_numCalleeRegisters(other.m_numCalleeRegisters)
, m_numVars(other.m_numVars)
, m_numCapturedVars(other.m_numCapturedVars)
, m_isConstructor(other.m_isConstructor)
, m_ownerExecutable(*other.m_globalData, other.m_ownerExecutable.get(), other.m_ownerExecutable.get())
, m_globalData(other.m_globalData)
......@@ -1775,7 +1774,6 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, CodeType codeType, JSGlo
, m_heap(&m_globalObject->globalData().heap)
, m_numCalleeRegisters(0)
, m_numVars(0)
, m_numCapturedVars(0)
, m_isConstructor(isConstructor)
, m_numParameters(0)
, m_ownerExecutable(globalObject->globalData(), ownerExecutable, ownerExecutable)
......
......@@ -516,7 +516,7 @@ namespace JSC {
m_argumentsRegister = argumentsRegister;
ASSERT(usesArguments());
}
int argumentsRegister()
int argumentsRegister() const
{
ASSERT(usesArguments());
return m_argumentsRegister;
......@@ -531,7 +531,7 @@ namespace JSC {
{
m_activationRegister = activationRegister;
}
int activationRegister()
int activationRegister() const
{
ASSERT(needsFullScopeChain());
return m_activationRegister;
......@@ -554,11 +554,24 @@ namespace JSC {
if (inlineCallFrame && !operandIsArgument(operand))
return inlineCallFrame->capturedVars.get(operand);
// Our estimate of argument capture is conservative.
if (operandIsArgument(operand))
return needsActivation() || usesArguments();
return usesArguments();
return operand < m_numCapturedVars;
// The activation object isn't in the captured region, but it's "captured"
// in the sense that stores to its location can be observed indirectly.
if (needsActivation() && operand == activationRegister())
return true;
// Ditto for the arguments object.
if (usesArguments() && operand == argumentsRegister())
return true;
// Ditto for the arguments object.
if (usesArguments() && operand == unmodifiedArgumentsRegister(argumentsRegister()))
return true;
return operand >= m_symbolTable->captureStart()
&& operand < m_symbolTable->captureEnd();
}
CodeType codeType() const { return m_codeType; }
......@@ -1176,7 +1189,6 @@ namespace JSC {
int m_numCalleeRegisters;
int m_numVars;
int m_numCapturedVars;
bool m_isConstructor;
protected:
......@@ -1532,7 +1544,7 @@ namespace JSC {
return CallFrame::argumentOffset(argument);
ASSERT(slowArguments[argument].status == SlowArgument::Captured);
return slowArguments[argument].indexIfCaptured;
return slowArguments[argument].index;
}
inline Register& ExecState::r(int index)
......
......@@ -304,8 +304,6 @@ BytecodeGenerator::BytecodeGenerator(ProgramNode* programNode, JSScope* scope, S
// FIXME: Move code that modifies the global object to Interpreter::execute.
codeBlock->m_numCapturedVars = codeBlock->m_numVars;
if (compilationKind == OptimizingCompilation)
return;
......@@ -392,6 +390,8 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, JSScope* sc
m_codeBlock->setActivationRegister(m_activationRegister->index());
}
symbolTable->setCaptureStart(m_codeBlock->m_numVars);
if (functionBody->usesArguments() || codeBlock->usesEval() || m_shouldEmitDebugHooks) { // May reify arguments object.
RegisterID* unmodifiedArgumentsRegister = addVar(); // Anonymous, so it can't be modified by user code.
RegisterID* argumentsRegister = addVar(propertyNames().arguments, false); // Can be changed by assigning to 'arguments'.
......@@ -423,32 +423,33 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, JSScope* sc
}
}
bool mayReifyArgumentsObject = codeBlock->usesArguments() || codeBlock->usesEval() || m_shouldEmitDebugHooks;
bool shouldCaptureAllTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval();
bool capturesAnyArgumentByName = false;
if (functionBody->hasCapturedVariables()) {
Vector<RegisterID*> capturedArguments;
if (functionBody->hasCapturedVariables() || shouldCaptureAllTheThings) {
FunctionParameters& parameters = *functionBody->parameters();
capturedArguments.resize(parameters.size());
for (size_t i = 0; i < parameters.size(); ++i) {
if (!functionBody->captures(parameters[i]))
capturedArguments[i] = 0;
if (!functionBody->captures(parameters[i]) && !shouldCaptureAllTheThings)
continue;
capturesAnyArgumentByName = true;
break;
capturedArguments[i] = addVar();
}
}
if (mayReifyArgumentsObject || capturesAnyArgumentByName) {
symbolTable->setCaptureMode(SharedSymbolTable::AllOfTheThings);
symbolTable->setCaptureStart(-CallFrame::offsetFor(symbolTable->parameterCountIncludingThis()));
} else {
symbolTable->setCaptureMode(SharedSymbolTable::SomeOfTheThings);
symbolTable->setCaptureStart(m_codeBlock->m_numVars);
}
if (mayReifyArgumentsObject && capturesAnyArgumentByName) {
if (capturesAnyArgumentByName && !codeBlock->isStrictMode()) {
size_t parameterCount = symbolTable->parameterCount();
OwnArrayPtr<SlowArgument> slowArguments = adoptArrayPtr(new SlowArgument[parameterCount]);
for (size_t i = 0; i < parameterCount; ++i) {
if (!capturedArguments[i]) {
ASSERT(slowArguments[i].status == SlowArgument::Normal);
slowArguments[i].index = CallFrame::argumentOffset(i);
continue;
}
slowArguments[i].status = SlowArgument::Captured;
slowArguments[i].indexIfCaptured = CallFrame::argumentOffset(i);
slowArguments[i].index = capturedArguments[i]->index();
}
symbolTable->setSlowArguments(slowArguments.release());
}
......@@ -491,7 +492,7 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, JSScope* sc
instructions().append(m_activationRegister->index());
}
codeBlock->m_numCapturedVars = codeBlock->m_numVars;
symbolTable->setCaptureEnd(codeBlock->m_numVars);
m_firstLazyFunction = codeBlock->m_numVars;
for (size_t i = 0; i < functionStack.size(); ++i) {
......@@ -518,10 +519,8 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, JSScope* sc
addVar(ident, varStack[i].second & DeclarationStacks::IsConstant);
}
if (m_shouldEmitDebugHooks || codeBlock->usesEval())
codeBlock->m_numCapturedVars = codeBlock->m_numVars;
symbolTable->setCaptureEnd(codeBlock->m_numCapturedVars);
if (shouldCaptureAllTheThings)
symbolTable->setCaptureEnd(codeBlock->m_numVars);
FunctionParameters& parameters = *functionBody->parameters();
m_parameters.grow(parameters.size() + 1); // reserve space for "this"
......@@ -531,9 +530,16 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, JSScope* sc
m_thisRegister.setIndex(nextParameterIndex--);
m_codeBlock->addParameter();
for (size_t i = 0; i < parameters.size(); ++i)
addParameter(parameters[i], nextParameterIndex--);
for (size_t i = 0; i < parameters.size(); ++i, --nextParameterIndex) {
int index = nextParameterIndex;
if (capturedArguments.size() && capturedArguments[i]) {
ASSERT((functionBody->hasCapturedVariables() && functionBody->captures(parameters[i])) || shouldCaptureAllTheThings);
index = capturedArguments[i]->index();
RegisterID original(nextParameterIndex);
emitMove(capturedArguments[i], &original);
}
addParameter(parameters[i], index);
}
preserveLastVar();
// We declare the callee's name last because it should lose to a var, function, and/or parameter declaration.
......@@ -603,7 +609,6 @@ BytecodeGenerator::BytecodeGenerator(EvalNode* evalNode, JSScope* scope, SharedS
for (size_t i = 0; i < numVariables; ++i)
variables.append(*varStack[i].first);
codeBlock->adoptVariables(variables);
codeBlock->m_numCapturedVars = codeBlock->m_numVars;
preserveLastVar();
}
......
......@@ -467,8 +467,11 @@ private:
numArguments = m_inlineStackTop->m_codeBlock->numParameters();
for (unsigned argument = numArguments; argument-- > 1;)
flush(argumentToOperand(argument));
for (unsigned local = m_inlineStackTop->m_codeBlock->m_numCapturedVars; local--;)
for (int local = 0; local < m_inlineStackTop->m_codeBlock->m_numVars; ++local) {
if (!m_inlineStackTop->m_codeBlock->isCaptured(local))
continue;
flush(local);
}
}
// Get an operand, and perform a ToInt32/ToNumber conversion on it.
......@@ -3282,10 +3285,10 @@ void ByteCodeParser::parseCodeBlock()
CodeBlock* codeBlock = m_inlineStackTop->m_codeBlock;
#if DFG_ENABLE(DEBUG_VERBOSE)
dataLog("Parsing code block %p. codeType = %s, numCapturedVars = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n",
dataLog("Parsing code block %p. codeType = %s, captureCount = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n",
codeBlock,
codeTypeToString(codeBlock->codeType()),
codeBlock->m_numCapturedVars,
codeBlock->symbolTable()->captureCount(),
codeBlock->needsFullScopeChain()?"true":"false",
codeBlock->ownerExecutable()->needsActivation()?"true":"false",
codeBlock->ownerExecutable()->isStrictMode()?"true":"false");
......
......@@ -4215,7 +4215,7 @@ void SpeculativeJIT::compile(Node& node)
m_jit.load32(
JITCompiler::BaseIndex(
resultPayloadGPR, indexGPR, JITCompiler::TimesEight,
OBJECT_OFFSETOF(SlowArgument, indexIfCaptured)),
OBJECT_OFFSETOF(SlowArgument, index)),
resultPayloadGPR);
m_jit.load32(
......@@ -4292,7 +4292,7 @@ void SpeculativeJIT::compile(Node& node)
m_jit.load32(
JITCompiler::BaseIndex(
resultPayloadGPR, indexGPR, JITCompiler::TimesEight,
OBJECT_OFFSETOF(SlowArgument, indexIfCaptured)),
OBJECT_OFFSETOF(SlowArgument, index)),
resultPayloadGPR);
m_jit.load32(
JITCompiler::BaseIndex(
......
......@@ -4164,7 +4164,7 @@ void SpeculativeJIT::compile(Node& node)
m_jit.load32(
JITCompiler::BaseIndex(
resultGPR, indexGPR, JITCompiler::TimesEight,
OBJECT_OFFSETOF(SlowArgument, indexIfCaptured)),
OBJECT_OFFSETOF(SlowArgument, index)),
resultGPR);
m_jit.signExtend32ToPtr(resultGPR, resultGPR);
m_jit.loadPtr(
......@@ -4229,7 +4229,7 @@ void SpeculativeJIT::compile(Node& node)
m_jit.load32(
JITCompiler::BaseIndex(
resultGPR, indexGPR, JITCompiler::TimesEight,
OBJECT_OFFSETOF(SlowArgument, indexIfCaptured)),
OBJECT_OFFSETOF(SlowArgument, index)),
resultGPR);
m_jit.signExtend32ToPtr(resultGPR, resultGPR);
m_jit.loadPtr(
......
......@@ -834,6 +834,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J
{
JSScope* scope = callFrame->scope();
ASSERT(isValidThisObject(thisObj, callFrame));
scope->globalData()->exception = JSValue();
ASSERT(!scope->globalData()->exception);
ASSERT(!callFrame->globalData().isCollectorBusy());
if (callFrame->globalData().isCollectorBusy())
......
......@@ -343,6 +343,18 @@ void Arguments::tearOff(CallFrame* callFrame)
m_registerArray = adoptArrayPtr(new WriteBarrier<Unknown>[m_numArguments]);
m_registers = m_registerArray.get() + CallFrame::offsetFor(m_numArguments + 1);
// If we have a captured argument that logically aliases activation storage,
// but we optimize away the activation, the argument needs to tear off into
// our storage. The simplest way to do this is to revert it to Normal status.
if (m_slowArguments && !m_activation) {
for (size_t i = 0; i < m_numArguments; ++i) {
if (m_slowArguments[i].status != SlowArgument::Captured)
continue;
m_slowArguments[i].status = SlowArgument::Normal;
m_slowArguments[i].index = CallFrame::argumentOffset(i);
}
}
if (!callFrame->isInlineCallFrame()) {
for (size_t i = 0; i < m_numArguments; ++i)
trySetArgument(callFrame->globalData(), i, callFrame->argumentAfterCapture(i));
......@@ -362,20 +374,8 @@ void Arguments::didTearOffActivation(ExecState* exec, JSActivation* activation)
if (!m_numArguments)
return;
tearOff(exec);
SharedSymbolTable* symbolTable = activation->symbolTable();
const SlowArgument* slowArguments = symbolTable->slowArguments();
if (!slowArguments)
return;
ASSERT(symbolTable->captureMode() == SharedSymbolTable::AllOfTheThings);
m_activation.set(exec->globalData(), this, activation);
allocateSlowArguments();
size_t count = min<unsigned>(m_numArguments, symbolTable->parameterCount());
for (size_t i = 0; i < count; ++i)
m_slowArguments[i] = slowArguments[i];
tearOff(exec);
}
void Arguments::tearOff(CallFrame* callFrame, InlineCallFrame* inlineCallFrame)
......
......@@ -161,6 +161,10 @@ namespace JSC {
if (m_slowArguments)
return;
m_slowArguments = adoptArrayPtr(new SlowArgument[m_numArguments]);
for (size_t i = 0; i < m_numArguments; ++i) {
ASSERT(m_slowArguments[i].status == SlowArgument::Normal);
m_slowArguments[i].index = CallFrame::argumentOffset(i);
}
}
inline bool Arguments::tryDeleteArgument(size_t argument)
......@@ -210,14 +214,14 @@ namespace JSC {
inline WriteBarrierBase<Unknown>& Arguments::argument(size_t argument)
{
ASSERT(isArgument(argument));
if (!m_slowArguments || m_slowArguments[argument].status == SlowArgument::Normal)
if (!m_slowArguments)
return m_registers[CallFrame::argumentOffset(argument)];
ASSERT(m_slowArguments[argument].status == SlowArgument::Captured);
if (!m_activation)
return m_registers[m_slowArguments[argument].indexIfCaptured];
int index = m_slowArguments[argument].index;
if (!m_activation || m_slowArguments[argument].status != SlowArgument::Captured)
return m_registers[index];
return m_activation->registerAt(m_slowArguments[argument].indexIfCaptured);
return m_activation->registerAt(index);
}
inline void Arguments::finishCreation(CallFrame* callFrame)
......@@ -234,6 +238,15 @@ namespace JSC {
m_overrodeCaller = false;
m_isStrictMode = callFrame->codeBlock()->isStrictMode();
SharedSymbolTable* symbolTable = callFrame->codeBlock()->symbolTable();
const SlowArgument* slowArguments = symbolTable->slowArguments();
if (slowArguments) {
allocateSlowArguments();
size_t count = std::min<unsigned>(m_numArguments, symbolTable->parameterCount());
for (size_t i = 0; i < count; ++i)
m_slowArguments[i] = slowArguments[i];
}
// The bytecode generator omits op_tear_off_activation in cases of no
// declared parameters, so we need to tear off immediately.
if (m_isStrictMode || !callee->jsExecutable()->parameterCount())
......@@ -254,6 +267,8 @@ namespace JSC {
m_overrodeCaller = false;
m_isStrictMode = jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->isStrictMode();
ASSERT(!jsCast<FunctionExecutable*>(inlineCallFrame->executable.get())->symbolTable()->slowArguments());
// The bytecode generator omits op_tear_off_activation in cases of no
// declared parameters, so we need to tear off immediately.
if (m_isStrictMode || !callee->jsExecutable()->parameterCount())
......
......@@ -135,7 +135,6 @@ const ClassInfo FunctionExecutable::s_info = { "FunctionExecutable", &ScriptExec
FunctionExecutable::FunctionExecutable(JSGlobalData& globalData, FunctionBodyNode* node)
: ScriptExecutable(globalData.functionExecutableStructure.get(), globalData, node->source(), node->isStrictMode())
, m_numCapturedVariables(0)
, m_forceUsesArguments(node->usesArguments())
, m_parameters(node->parameters())
, m_name(node->ident())
......@@ -527,7 +526,6 @@ JSObject* FunctionExecutable::compileForCallInternal(ExecState* exec, JSScope* s
m_numParametersForCall = m_codeBlockForCall->numParameters();
ASSERT(m_numParametersForCall);
m_numCapturedVariables = m_codeBlockForCall->m_numCapturedVars;
m_symbolTable.set(exec->globalData(), this, m_codeBlockForCall->symbolTable());
#if ENABLE(JIT)
......@@ -570,7 +568,6 @@ JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, JSSco
m_numParametersForConstruct = m_codeBlockForConstruct->numParameters();
ASSERT(m_numParametersForConstruct);
m_numCapturedVariables = m_codeBlockForConstruct->m_numCapturedVars;
m_symbolTable.set(exec->globalData(), this, m_codeBlockForConstruct->symbolTable());
#if ENABLE(JIT)
......
......@@ -692,7 +692,6 @@ namespace JSC {
const Identifier& inferredName() { return m_inferredName; }
JSString* nameValue() const { return m_nameValue.get(); }
size_t parameterCount() const { return m_parameters->size(); } // Excluding 'this'!
unsigned capturedVariableCount() const { return m_numCapturedVariables; }
String paramString() const;