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

Refactored op_tear_off* to support activations that don't allocate space for 'arguments'

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

Reviewed by Gavin Barraclough.

This is a step toward smaller activations.

As a side-effect, this patch eliminates a load and branch from the hot path
of activation tear-off by moving it to the cold path of arguments tear-off. Our
optimizing assumptions are that activations are common and that reifying the
arguments object is less common.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* bytecode/Opcode.h:
(JSC::padOpcodeName): Updated for new opcode lengths.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::addConstantValue): Added support for JSValue()
in the bytecode, which we use when we have 'arguments' but no activation.

(JSC::BytecodeGenerator::emitReturn): Always emit tear_off_arguments
if we've allocated the arguments registers. This allows tear_off_activation
not to worry about the arguments object anymore.

Also, pass the activation and arguments values directly to these opcodes
instead of requiring the opcodes to infer the values through special
registers. This gives us more flexibility to move or eliminate registers.

* dfg/DFGArgumentsSimplificationPhase.cpp:
(JSC::DFG::ArgumentsSimplificationPhase::run):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGNode.h:
(Node): Updated for new opcode lengths.

* dfg/DFGOperations.cpp: Activation tear-off doesn't worry about the
arguments object anymore. If 'arguments' is in use and reified, it's
responsible for aliasing back to the activation object in tear_off_arguments.

* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(SpeculativeJIT):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile): Don't pass the arguments object to
activation tear-off; do pass the activation object to arguments tear-off.

* interpreter/Interpreter.cpp:
(JSC::Interpreter::privateExecute): Ditto.

* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_tear_off_activation):
(JSC::JIT::emit_op_tear_off_arguments):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_tear_off_activation):
(JSC::JIT::emit_op_tear_off_arguments):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm: Same change in a few more execution engines.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@128096 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 29cd8c3c
2012-09-09 Geoffrey Garen <ggaren@apple.com>
Refactored op_tear_off* to support activations that don't allocate space for 'arguments'
https://bugs.webkit.org/show_bug.cgi?id=96231
Reviewed by Gavin Barraclough.
This is a step toward smaller activations.
As a side-effect, this patch eliminates a load and branch from the hot path
of activation tear-off by moving it to the cold path of arguments tear-off. Our
optimizing assumptions are that activations are common and that reifying the
arguments object is less common.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* bytecode/Opcode.h:
(JSC::padOpcodeName): Updated for new opcode lengths.
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::addConstantValue): Added support for JSValue()
in the bytecode, which we use when we have 'arguments' but no activation.
(JSC::BytecodeGenerator::emitReturn): Always emit tear_off_arguments
if we've allocated the arguments registers. This allows tear_off_activation
not to worry about the arguments object anymore.
Also, pass the activation and arguments values directly to these opcodes
instead of requiring the opcodes to infer the values through special
registers. This gives us more flexibility to move or eliminate registers.
* dfg/DFGArgumentsSimplificationPhase.cpp:
(JSC::DFG::ArgumentsSimplificationPhase::run):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGNode.h:
(Node): Updated for new opcode lengths.
* dfg/DFGOperations.cpp: Activation tear-off doesn't worry about the
arguments object anymore. If 'arguments' is in use and reified, it's
responsible for aliasing back to the activation object in tear_off_arguments.
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(SpeculativeJIT):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile): Don't pass the arguments object to
activation tear-off; do pass the activation object to arguments tear-off.
* interpreter/Interpreter.cpp:
(JSC::Interpreter::privateExecute): Ditto.
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_tear_off_activation):
(JSC::JIT::emit_op_tear_off_arguments):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_tear_off_activation):
(JSC::JIT::emit_op_tear_off_arguments):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm: Same change in a few more execution engines.
2012-09-10 Patrick Gansterer <paroga@webkit.org>
 
[JSC] Use StringBuilder::appendNumber() instead of String::number()
......
......@@ -1407,14 +1407,14 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
}
case op_tear_off_activation: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
dataLog("[%4d] tear_off_activation\t %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data());
dataLog("[%4d] tear_off_activation\t %s", location, registerName(exec, r0).data());
dumpBytecodeCommentAndNewLine(location);
break;
}
case op_tear_off_arguments: {
int r0 = (++it)->u.operand;
dataLog("[%4d] tear_off_arguments %s", location, registerName(exec, r0).data());
int r1 = (++it)->u.operand;
dataLog("[%4d] tear_off_arguments %s, %s", location, registerName(exec, r0).data(), registerName(exec, r1).data());
dumpBytecodeCommentAndNewLine(location);
break;
}
......
......@@ -174,8 +174,8 @@ namespace JSC {
macro(op_call, 6) \
macro(op_call_eval, 6) \
macro(op_call_varargs, 5) \
macro(op_tear_off_activation, 3) \
macro(op_tear_off_arguments, 2) \
macro(op_tear_off_activation, 2) \
macro(op_tear_off_arguments, 3) \
macro(op_ret, 2) \
macro(op_call_put_result, 3) /* has value profiling */ \
macro(op_ret_object_or_this, 3) \
......
......@@ -272,6 +272,7 @@ BytecodeGenerator::BytecodeGenerator(ProgramNode* programNode, JSScope* scope, S
, m_scopeNode(programNode)
, m_codeBlock(codeBlock)
, m_thisRegister(CallFrame::thisArgumentOffset())
, m_emptyValueRegister(0)
, m_finallyDepth(0)
, m_dynamicScopeDepth(0)
, m_baseScopeDepth(0)
......@@ -353,6 +354,7 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, JSScope* sc
, m_scopeNode(functionBody)
, m_codeBlock(codeBlock)
, m_activationRegister(0)
, m_emptyValueRegister(0)
, m_finallyDepth(0)
, m_dynamicScopeDepth(0)
, m_baseScopeDepth(0)
......@@ -386,8 +388,6 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, JSScope* sc
m_codeBlock->setActivationRegister(m_activationRegister->index());
}
// Both op_tear_off_activation and op_tear_off_arguments tear off the 'arguments'
// object, if created.
if (m_codeBlock->needsFullScopeChain() || functionBody->usesArguments()) {
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'.
......@@ -526,6 +526,7 @@ BytecodeGenerator::BytecodeGenerator(EvalNode* evalNode, JSScope* scope, SymbolT
, m_scopeNode(evalNode)
, m_codeBlock(codeBlock)
, m_thisRegister(CallFrame::thisArgumentOffset())
, m_emptyValueRegister(0)
, m_finallyDepth(0)
, m_dynamicScopeDepth(0)
, m_baseScopeDepth(codeBlock->baseScopeDepth())
......@@ -1111,18 +1112,33 @@ unsigned BytecodeGenerator::addConstant(const Identifier& ident)
return result.iterator->second;
}
// We can't hash JSValue(), so we use a dedicated data member to cache it.
RegisterID* BytecodeGenerator::addConstantEmptyValue()
{
if (!m_emptyValueRegister) {
int index = m_nextConstantOffset;
m_constantPoolRegisters.append(FirstConstantRegisterIndex + m_nextConstantOffset);
++m_nextConstantOffset;
m_codeBlock->addConstant(JSValue());
m_emptyValueRegister = &m_constantPoolRegisters[index];
}
return m_emptyValueRegister;
}
RegisterID* BytecodeGenerator::addConstantValue(JSValue v)
{
int index = m_nextConstantOffset;
if (!v)
return addConstantEmptyValue();
int index = m_nextConstantOffset;
JSValueMap::AddResult result = m_jsValueMap.add(JSValue::encode(v), m_nextConstantOffset);
if (result.isNewEntry) {
m_constantPoolRegisters.append(FirstConstantRegisterIndex + m_nextConstantOffset);
++m_nextConstantOffset;
m_codeBlock->addConstant(JSValue(v));
m_codeBlock->addConstant(v);
} else
index = result.iterator->second;
return &m_constantPoolRegisters[index];
}
......@@ -2046,10 +2062,12 @@ RegisterID* BytecodeGenerator::emitReturn(RegisterID* src)
if (m_codeBlock->needsFullScopeChain()) {
emitOpcode(op_tear_off_activation);
instructions().append(m_activationRegister->index());
instructions().append(m_codeBlock->argumentsRegister());
} else if (m_codeBlock->usesArguments() && m_codeBlock->numParameters() != 1 && !m_codeBlock->isStrictMode()) {
}
if (m_codeBlock->usesArguments() && m_codeBlock->numParameters() != 1 && !m_codeBlock->isStrictMode()) {
emitOpcode(op_tear_off_arguments);
instructions().append(m_codeBlock->argumentsRegister());
instructions().append(m_activationRegister ? m_activationRegister->index() : emitLoad(0, JSValue())->index());
}
// Constructors use op_ret_object_or_this to check the result is an
......
......@@ -637,6 +637,7 @@ namespace JSC {
unsigned addConstant(const Identifier&);
RegisterID* addConstantValue(JSValue);
RegisterID* addConstantEmptyValue();
unsigned addRegExp(RegExp*);
unsigned addConstantBuffer(unsigned length);
......@@ -713,6 +714,7 @@ namespace JSC {
RegisterID m_thisRegister;
RegisterID m_calleeRegister;
RegisterID* m_activationRegister;
RegisterID* m_emptyValueRegister;
SegmentedVector<RegisterID, 32> m_constantPoolRegisters;
SegmentedVector<RegisterID, 32> m_calleeRegisters;
SegmentedVector<RegisterID, 32> m_parameters;
......
......@@ -616,6 +616,7 @@ public:
node.setOpAndDefaultFlags(Nop);
m_graph.clearAndDerefChild1(node);
m_graph.clearAndDerefChild2(node);
node.setRefCount(0);
break;
}
......
......@@ -2855,13 +2855,13 @@ bool ByteCodeParser::parseBlock(unsigned limit)
}
case op_tear_off_activation: {
addToGraph(TearOffActivation, OpInfo(unmodifiedArgumentsRegister(currentInstruction[2].u.operand)), get(currentInstruction[1].u.operand), get(currentInstruction[2].u.operand));
addToGraph(TearOffActivation, get(currentInstruction[1].u.operand));
NEXT_OPCODE(op_tear_off_activation);
}
case op_tear_off_arguments: {
m_graph.m_hasArguments = true;
addToGraph(TearOffArguments, get(unmodifiedArgumentsRegister(currentInstruction[1].u.operand)));
addToGraph(TearOffArguments, get(unmodifiedArgumentsRegister(currentInstruction[1].u.operand)), get(currentInstruction[2].u.operand));
NEXT_OPCODE(op_tear_off_arguments);
}
......
......@@ -337,12 +337,6 @@ struct Node {
return variableAccessData()->local();
}
VirtualRegister unmodifiedArgumentsRegister()
{
ASSERT(op() == TearOffActivation);
return static_cast<VirtualRegister>(m_opInfo);
}
VirtualRegister unlinkedLocal()
{
ASSERT(op() == GetLocalUnlinked);
......
......@@ -1165,40 +1165,28 @@ JSCell* DFG_OPERATION operationCreateInlinedArguments(
return result;
}
void DFG_OPERATION operationTearOffActivation(ExecState* exec, JSCell* activationCell, int32_t unmodifiedArgumentsRegister)
void DFG_OPERATION operationTearOffActivation(ExecState* exec, JSCell* activationCell)
{
JSGlobalData& globalData = exec->globalData();
NativeCallFrameTracer tracer(&globalData, exec);
if (!activationCell) {
if (JSValue v = exec->uncheckedR(unmodifiedArgumentsRegister).jsValue()) {
if (!exec->codeBlock()->isStrictMode())
asArguments(v)->tearOff(exec);
}
return;
}
JSActivation* activation = jsCast<JSActivation*>(activationCell);
activation->tearOff(exec->globalData());
if (JSValue v = exec->uncheckedR(unmodifiedArgumentsRegister).jsValue())
asArguments(v)->didTearOffActivation(exec->globalData(), activation);
jsCast<JSActivation*>(activationCell)->tearOff(exec->globalData());
}
void DFG_OPERATION operationTearOffArguments(ExecState* exec, JSCell* argumentsCell)
void DFG_OPERATION operationTearOffArguments(ExecState* exec, JSCell* argumentsCell, JSCell* activationCell)
{
ASSERT(exec->codeBlock()->usesArguments());
ASSERT(!exec->codeBlock()->needsFullScopeChain());
asArguments(argumentsCell)->tearOff(exec);
if (activationCell) {
jsCast<Arguments*>(argumentsCell)->didTearOffActivation(exec->globalData(), jsCast<JSActivation*>(activationCell));
return;
}
jsCast<Arguments*>(argumentsCell)->tearOff(exec);
}
void DFG_OPERATION operationTearOffInlinedArguments(
ExecState* exec, JSCell* argumentsCell, InlineCallFrame* inlineCallFrame)
{
// This should only be called when the inline code block uses arguments but does not
// need a full scope chain. We could assert it, except that the assertion would be
// rather expensive and may cause side effects that would greatly diverge debug-mode
// behavior from release-mode behavior, since getting the code block of an inline
// call frame implies call frame reification.
asArguments(argumentsCell)->tearOff(exec, inlineCallFrame);
ExecState* exec, JSCell* argumentsCell, JSCell* activationCell, InlineCallFrame* inlineCallFrame)
{
ASSERT_UNUSED(activationCell, !activationCell); // Currently, we don't inline functions with activations.
jsCast<Arguments*>(argumentsCell)->tearOff(exec, inlineCallFrame);
}
EncodedJSValue DFG_OPERATION operationGetArgumentsLength(ExecState* exec, int32_t argumentsRegister)
......
......@@ -98,8 +98,10 @@ typedef size_t DFG_OPERATION (*S_DFGOperation_J)(EncodedJSValue);
typedef void DFG_OPERATION (*V_DFGOperation_EAZJ)(ExecState*, JSArray*, int32_t, EncodedJSValue);
typedef void DFG_OPERATION (*V_DFGOperation_EC)(ExecState*, JSCell*);
typedef void DFG_OPERATION (*V_DFGOperation_ECIcf)(ExecState*, JSCell*, InlineCallFrame*);
typedef void DFG_OPERATION (*V_DFGOperation_ECCIcf)(ExecState*, JSCell*, JSCell*, InlineCallFrame*);
typedef void DFG_OPERATION (*V_DFGOperation_ECJJ)(ExecState*, JSCell*, EncodedJSValue, EncodedJSValue);
typedef void DFG_OPERATION (*V_DFGOperation_ECZ)(ExecState*, JSCell*, int32_t);
typedef void DFG_OPERATION (*V_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*);
typedef void DFG_OPERATION (*V_DFGOperation_EJCI)(ExecState*, EncodedJSValue, JSCell*, Identifier*);
typedef void DFG_OPERATION (*V_DFGOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
typedef void DFG_OPERATION (*V_DFGOperation_EJPP)(ExecState*, EncodedJSValue, EncodedJSValue, void*);
......@@ -173,9 +175,9 @@ char* DFG_OPERATION operationLinkConstruct(ExecState*) WTF_INTERNAL;
JSCell* DFG_OPERATION operationCreateActivation(ExecState*) WTF_INTERNAL;
JSCell* DFG_OPERATION operationCreateArguments(ExecState*) WTF_INTERNAL;
JSCell* DFG_OPERATION operationCreateInlinedArguments(ExecState*, InlineCallFrame*) WTF_INTERNAL;
void DFG_OPERATION operationTearOffActivation(ExecState*, JSCell*, int32_t unmodifiedArgumentsRegister) WTF_INTERNAL;
void DFG_OPERATION operationTearOffArguments(ExecState*, JSCell*) WTF_INTERNAL;
void DFG_OPERATION operationTearOffInlinedArguments(ExecState*, JSCell*, InlineCallFrame*) WTF_INTERNAL;
void DFG_OPERATION operationTearOffActivation(ExecState*, JSCell*) WTF_INTERNAL;
void DFG_OPERATION operationTearOffArguments(ExecState*, JSCell*, JSCell*) WTF_INTERNAL;
void DFG_OPERATION operationTearOffInlinedArguments(ExecState*, JSCell*, JSCell*, InlineCallFrame*) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationGetArgumentsLength(ExecState*, int32_t) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationGetInlinedArgumentByVal(ExecState*, int32_t, InlineCallFrame*, int32_t) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationGetArgumentByVal(ExecState*, int32_t, int32_t) WTF_INTERNAL;
......
......@@ -1353,6 +1353,11 @@ public:
m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(arg2));
return appendCallWithExceptionCheck(operation);
}
JITCompiler::Call callOperation(V_DFGOperation_ECCIcf operation, GPRReg arg1, GPRReg arg2, InlineCallFrame* arg3)
{
m_jit.setupArgumentsWithExecState(arg1, arg2, TrustedImmPtr(arg3));
return appendCallWithExceptionCheck(operation);
}
JITCompiler::Call callOperation(V_DFGOperation_EJPP operation, GPRReg arg1, GPRReg arg2, void* pointer)
{
m_jit.setupArgumentsWithExecState(arg1, arg2, TrustedImmPtr(pointer));
......@@ -1388,6 +1393,11 @@ public:
m_jit.setupArgumentsWithExecState(arg1, TrustedImm32(arg2));
return appendCallWithExceptionCheck(operation);
}
JITCompiler::Call callOperation(V_DFGOperation_ECC operation, GPRReg arg1, GPRReg arg2)
{
m_jit.setupArgumentsWithExecState(arg1, arg2);
return appendCallWithExceptionCheck(operation);
}
JITCompiler::Call callOperation(V_DFGOperation_W operation, WatchpointSet* watchpointSet)
{
m_jit.setupArguments(TrustedImmPtr(watchpointSet));
......@@ -1651,6 +1661,11 @@ public:
m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(inlineCallFrame));
return appendCallWithExceptionCheck(operation);
}
JITCompiler::Call callOperation(V_DFGOperation_ECCIcf operation, GPRReg arg1, GPRReg arg2, InlineCallFrame* inlineCallFrame)
{
m_jit.setupArgumentsWithExecState(arg1, arg2, TrustedImmPtr(inlineCallFrame));
return appendCallWithExceptionCheck(operation);
}
JITCompiler::Call callOperation(V_DFGOperation_EJPP operation, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2, void* pointer)
{
m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2, TrustedImmPtr(pointer));
......@@ -1671,6 +1686,11 @@ public:
m_jit.setupArgumentsWithExecState(arg1, TrustedImm32(arg2));
return appendCallWithExceptionCheck(operation);
}
JITCompiler::Call callOperation(V_DFGOperation_ECC operation, GPRReg arg1, GPRReg arg2)
{
m_jit.setupArgumentsWithExecState(arg1, arg2);
return appendCallWithExceptionCheck(operation);
}
JITCompiler::Call callOperation(V_DFGOperation_EPZJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3Tag, GPRReg arg3Payload)
{
m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag);
......@@ -1696,6 +1716,11 @@ public:
{
return callOperation(operation, arg1, arg2);
}
template<typename FunctionType, typename ArgumentType1, typename ArgumentType2, typename ArgumentType3>
JITCompiler::Call callOperation(FunctionType operation, NoResultTag, ArgumentType1 arg1, ArgumentType2 arg2, ArgumentType3 arg3)
{
return callOperation(operation, arg1, arg2, arg3);
}
template<typename FunctionType, typename ArgumentType1, typename ArgumentType2, typename ArgumentType3, typename ArgumentType4>
JITCompiler::Call callOperation(FunctionType operation, NoResultTag, ArgumentType1 arg1, ArgumentType2 arg2, ArgumentType3 arg3, ArgumentType4 arg4)
{
......
......@@ -4023,43 +4023,39 @@ void SpeculativeJIT::compile(Node& node)
case TearOffActivation: {
JSValueOperand activationValue(this, node.child1());
JSValueOperand argumentsValue(this, node.child2());
GPRReg activationValueTagGPR = activationValue.tagGPR();
GPRReg activationValuePayloadGPR = activationValue.payloadGPR();
GPRReg argumentsValueTagGPR = argumentsValue.tagGPR();
JITCompiler::JumpList created;
created.append(m_jit.branch32(JITCompiler::NotEqual, activationValueTagGPR, TrustedImm32(JSValue::EmptyValueTag)));
created.append(m_jit.branch32(JITCompiler::NotEqual, argumentsValueTagGPR, TrustedImm32(JSValue::EmptyValueTag)));
JITCompiler::Jump created = m_jit.branch32(JITCompiler::NotEqual, activationValueTagGPR, TrustedImm32(JSValue::EmptyValueTag));
addSlowPathGenerator(
slowPathCall(
created, this, operationTearOffActivation, NoResult, activationValuePayloadGPR,
static_cast<int32_t>(node.unmodifiedArgumentsRegister())));
created, this, operationTearOffActivation, NoResult, activationValuePayloadGPR));
noResult(m_compileIndex);
break;
}
case TearOffArguments: {
JSValueOperand argumentsValue(this, node.child1());
GPRReg argumentsValueTagGPR = argumentsValue.tagGPR();
GPRReg argumentsValuePayloadGPR = argumentsValue.payloadGPR();
JSValueOperand unmodifiedArgumentsValue(this, node.child1());
JSValueOperand activationValue(this, node.child2());
GPRReg unmodifiedArgumentsValuePayloadGPR = unmodifiedArgumentsValue.payloadGPR();
GPRReg activationValuePayloadGPR = activationValue.payloadGPR();
JITCompiler::Jump created = m_jit.branch32(
JITCompiler::NotEqual, argumentsValueTagGPR, TrustedImm32(JSValue::EmptyValueTag));
JITCompiler::Jump created = m_jit.branchTest32(
JITCompiler::NonZero, unmodifiedArgumentsValuePayloadGPR);
if (node.codeOrigin.inlineCallFrame) {
addSlowPathGenerator(
slowPathCall(
created, this, operationTearOffInlinedArguments, NoResult,
argumentsValuePayloadGPR, node.codeOrigin.inlineCallFrame));
unmodifiedArgumentsValuePayloadGPR, activationValuePayloadGPR, node.codeOrigin.inlineCallFrame));
} else {
addSlowPathGenerator(
slowPathCall(
created, this, operationTearOffArguments, NoResult,
argumentsValuePayloadGPR));
unmodifiedArgumentsValuePayloadGPR, activationValuePayloadGPR));
}
noResult(m_compileIndex);
......
......@@ -3972,43 +3972,40 @@ void SpeculativeJIT::compile(Node& node)
cellResult(resultGPR, m_compileIndex);
break;
}
case TearOffActivation: {
ASSERT(!node.codeOrigin.inlineCallFrame);
JSValueOperand activationValue(this, node.child1());
JSValueOperand argumentsValue(this, node.child2());
GPRReg activationValueGPR = activationValue.gpr();
GPRReg argumentsValueGPR = argumentsValue.gpr();
JITCompiler::JumpList created;
created.append(m_jit.branchTestPtr(JITCompiler::NonZero, activationValueGPR));
created.append(m_jit.branchTestPtr(JITCompiler::NonZero, argumentsValueGPR));
JITCompiler::Jump created = m_jit.branchTestPtr(JITCompiler::NonZero, activationValueGPR);
addSlowPathGenerator(
slowPathCall(
created, this, operationTearOffActivation, NoResult, activationValueGPR,
static_cast<int32_t>(node.unmodifiedArgumentsRegister())));
created, this, operationTearOffActivation, NoResult, activationValueGPR));
noResult(m_compileIndex);
break;
}
case TearOffArguments: {
JSValueOperand argumentsValue(this, node.child1());
GPRReg argumentsValueGPR = argumentsValue.gpr();
JITCompiler::Jump created = m_jit.branchTestPtr(JITCompiler::NonZero, argumentsValueGPR);
JSValueOperand unmodifiedArgumentsValue(this, node.child1());
JSValueOperand activationValue(this, node.child2());
GPRReg unmodifiedArgumentsValueGPR = unmodifiedArgumentsValue.gpr();
GPRReg activationValueGPR = activationValue.gpr();
JITCompiler::Jump created = m_jit.branchTestPtr(JITCompiler::NonZero, unmodifiedArgumentsValueGPR);
if (node.codeOrigin.inlineCallFrame) {
addSlowPathGenerator(
slowPathCall(
created, this, operationTearOffInlinedArguments, NoResult,
argumentsValueGPR, node.codeOrigin.inlineCallFrame));
unmodifiedArgumentsValueGPR, activationValueGPR, node.codeOrigin.inlineCallFrame));
} else {
addSlowPathGenerator(
slowPathCall(
created, this, operationTearOffArguments, NoResult, argumentsValueGPR));
created, this, operationTearOffArguments, NoResult, unmodifiedArgumentsValueGPR, activationValueGPR));
}
noResult(m_compileIndex);
......
......@@ -4475,50 +4475,43 @@ skip_id_custom_self:
goto vm_throw;
}
DEFINE_OPCODE(op_tear_off_activation) {
/* tear_off_activation activation(r) arguments(r)
/* tear_off_activation activation(r)
Copy locals and named parameters from the register file to the heap.
Point the bindings in 'activation' and 'arguments' to this new backing
store. (Note that 'arguments' may not have been created. If created,
'arguments' already holds a copy of any extra / unnamed parameters.)
Point the bindings in 'activation' to this new backing store.
This opcode appears before op_ret in functions that require full scope chains.
*/
int activation = vPC[1].u.operand;
int arguments = vPC[2].u.operand;
ASSERT(codeBlock->needsFullScopeChain());
JSValue activationValue = callFrame->r(activation).jsValue();
if (activationValue) {
if (activationValue)
asActivation(activationValue)->tearOff(*globalData);
if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue())
asArguments(argumentsValue)->didTearOffActivation(*globalData, asActivation(activationValue));
} else if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue()) {
if (!codeBlock->isStrictMode())
asArguments(argumentsValue)->tearOff(callFrame);
}
vPC += OPCODE_LENGTH(op_tear_off_activation);
NEXT_INSTRUCTION();
}
DEFINE_OPCODE(op_tear_off_arguments) {
/* tear_off_arguments arguments(r)
/* tear_off_arguments arguments(r) activation(r)
Copy named parameters from the register file to the heap. Point the
bindings in 'arguments' to this new backing store. (Note that
'arguments' may not have been created. If created, 'arguments' already
holds a copy of any extra / unnamed parameters.)
bindings in 'arguments' to this new backing store. (If 'activation'
was also copied to the heap, 'arguments' will point to its storage.)
This opcode appears before op_ret in functions that don't require full
scope chains, but do use 'arguments'.
*/
int src1 = vPC[1].u.operand;
ASSERT(!codeBlock->needsFullScopeChain() && codeBlock->ownerExecutable()->usesArguments());
if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(src1)).jsValue())
asArguments(arguments)->tearOff(callFrame);
int arguments = vPC[1].u.operand;
int activation = vPC[2].u.operand;
ASSERT(codeBlock->usesArguments());
if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue()) {
if (JSValue activationValue = callFrame->r(activation).jsValue())
asArguments(argumentsValue)->didTearOffActivation(callFrame->globalData(), asActivation(activationValue));
else
asArguments(argumentsValue)->tearOff(callFrame);
}
vPC += OPCODE_LENGTH(op_tear_off_arguments);
NEXT_INSTRUCTION();
......
......@@ -559,25 +559,23 @@ void JIT::emit_op_construct(Instruction* currentInstruction)
void JIT::emit_op_tear_off_activation(Instruction* currentInstruction)
{
unsigned activation = currentInstruction[1].u.operand;
unsigned arguments = currentInstruction[2].u.operand;
Jump activationCreated = branchTestPtr(NonZero, addressFor(activation));
Jump argumentsNotCreated = branchTestPtr(Zero, addressFor(arguments));
activationCreated.link(this);
int activation = currentInstruction[1].u.operand;
Jump activationNotCreated = branchTestPtr(Zero, addressFor(activation));
JITStubCall stubCall(this, cti_op_tear_off_activation);
stubCall.addArgument(activation, regT2);