Add new builtin opcode tailCallForwardArguments
https://bugs.webkit.org/show_bug.cgi?id=158666
Reviewed by Filip Pizlo.
We should support the ability to have a builtin forward its
arguments to a helper without allocating an arguments object. This
patch adds a new bytecode intrinsic @tailCallForwardArguments that
takes two values. The first is the target of the call and the
second is the new this value. This opcode will tail call to the
passed function without triggering an allocation of an arguments
object for the caller function.
In the LLInt and Baseline this function acts the same way a normal
tail call does. The bytecode will allocate a new stack frame
copying all the arguments of the caller function into the new
frame, along with the new this. Then when the actual call happens
the new frame is copied over the caller frame. While this is not
necessary, it allows the target function to have more arguments
than the caller function via arity fixup.
Once we get to the DFG we reuse existing DFG Nodes for forwarding
arguments, although there were some minor changes. This patch
swaps the meaning of the second and third children for each DFG
varargs node, exchanging the argmuments and this child,
respectively. It also makes the arguments child for each varargs
node, as well as the ForwardVarargs node optional. If the optional
child is missing, then forwarding node assumes that the arguments
for the node's inlineCallFrame should be used instead. Finally,
when inlining the target of an inlined
op_tail_call_forward_arguments we make sure the arguments of the
forwarding function are marked as non-unboxable since this would
normally be done by the caller's create arguments object node,
which does not exist in this case.
* bytecode/BytecodeIntrinsicRegistry.h:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CallLinkInfo.h:
(JSC::CallLinkInfo::callTypeFor):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::finishCreation):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitCallForwardArgumentsInTailPosition):
(JSC::BytecodeGenerator::emitCallVarargs):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_tailCallForwardArguments):
(JSC::BytecodeIntrinsicNode::emit_intrinsic_tryGetById):
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::getPredictionWithoutOSRExit):
(JSC::DFG::ByteCodeParser::handleCall):
(JSC::DFG::ByteCodeParser::handleVarargsCall):
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasArgumentsChild):
(JSC::DFG::Node::argumentsChild):
* dfg/DFGPreciseLocalClobberize.h:
(JSC::DFG::PreciseLocalClobberizeAdaptor::readTop):
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileForwardVarargs):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::emitCall):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::emitCall):
* dfg/DFGVarargsForwardingPhase.cpp:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargs):
(JSC::FTL::DFG::LowerDFGToB3::compileForwardVarargs):
* interpreter/Interpreter.cpp:
(JSC::sizeFrameForForwardArguments):
(JSC::setupForwardArgumentsFrame):
(JSC::setupForwardArgumentsFrameAndSetThis):
* interpreter/Interpreter.h:
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
* jit/JITCall.cpp:
(JSC::JIT::compileSetupVarargsFrame):
(JSC::JIT::compileOpCall):
(JSC::JIT::compileOpCallSlowCase):
(JSC::JIT::emit_op_tail_call_forward_arguments):
(JSC::JIT::emitSlow_op_tail_call_forward_arguments):
* jit/JITCall32_64.cpp:
(JSC::JIT::emitSlow_op_tail_call_forward_arguments):
(JSC::JIT::emit_op_tail_call_forward_arguments):
(JSC::JIT::compileSetupVarargsFrame):
(JSC::JIT::compileOpCall):
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
(JSC::LLInt::varargsSetup):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter.asm:
* tests/stress/tailCallForwardArguments.js: Added.
(putFuncToPrivateName.createBuiltin):
(putFuncToPrivateName):
(createTailCallForwardingFuncWith):
(baz):
(baz2):
(baz3):
(let.bodyText):
(baz4):
(baz5):
(arrayEq):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@202003 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 2d3e36c..7c68d67 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,123 @@
+2016-06-12 Keith Miller <keith_miller@apple.com>
+
+ Add new builtin opcode tailCallForwardArguments
+ https://bugs.webkit.org/show_bug.cgi?id=158666
+
+ Reviewed by Filip Pizlo.
+
+ We should support the ability to have a builtin forward its
+ arguments to a helper without allocating an arguments object. This
+ patch adds a new bytecode intrinsic @tailCallForwardArguments that
+ takes two values. The first is the target of the call and the
+ second is the new this value. This opcode will tail call to the
+ passed function without triggering an allocation of an arguments
+ object for the caller function.
+
+ In the LLInt and Baseline this function acts the same way a normal
+ tail call does. The bytecode will allocate a new stack frame
+ copying all the arguments of the caller function into the new
+ frame, along with the new this. Then when the actual call happens
+ the new frame is copied over the caller frame. While this is not
+ necessary, it allows the target function to have more arguments
+ than the caller function via arity fixup.
+
+ Once we get to the DFG we reuse existing DFG Nodes for forwarding
+ arguments, although there were some minor changes. This patch
+ swaps the meaning of the second and third children for each DFG
+ varargs node, exchanging the argmuments and this child,
+ respectively. It also makes the arguments child for each varargs
+ node, as well as the ForwardVarargs node optional. If the optional
+ child is missing, then forwarding node assumes that the arguments
+ for the node's inlineCallFrame should be used instead. Finally,
+ when inlining the target of an inlined
+ op_tail_call_forward_arguments we make sure the arguments of the
+ forwarding function are marked as non-unboxable since this would
+ normally be done by the caller's create arguments object node,
+ which does not exist in this case.
+
+ * bytecode/BytecodeIntrinsicRegistry.h:
+ * bytecode/BytecodeList.json:
+ * bytecode/BytecodeUseDef.h:
+ (JSC::computeUsesForBytecodeOffset):
+ (JSC::computeDefsForBytecodeOffset):
+ * bytecode/CallLinkInfo.h:
+ (JSC::CallLinkInfo::callTypeFor):
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode):
+ (JSC::CodeBlock::finishCreation):
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::emitCallForwardArgumentsInTailPosition):
+ (JSC::BytecodeGenerator::emitCallVarargs):
+ * bytecompiler/BytecodeGenerator.h:
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::BytecodeIntrinsicNode::emit_intrinsic_tailCallForwardArguments):
+ (JSC::BytecodeIntrinsicNode::emit_intrinsic_tryGetById):
+ * dfg/DFGArgumentsEliminationPhase.cpp:
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::getPredictionWithoutOSRExit):
+ (JSC::DFG::ByteCodeParser::handleCall):
+ (JSC::DFG::ByteCodeParser::handleVarargsCall):
+ (JSC::DFG::ByteCodeParser::handleInlining):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::hasArgumentsChild):
+ (JSC::DFG::Node::argumentsChild):
+ * dfg/DFGPreciseLocalClobberize.h:
+ (JSC::DFG::PreciseLocalClobberizeAdaptor::readTop):
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileForwardVarargs):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::emitCall):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::emitCall):
+ * dfg/DFGVarargsForwardingPhase.cpp:
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargs):
+ (JSC::FTL::DFG::LowerDFGToB3::compileForwardVarargs):
+ * interpreter/Interpreter.cpp:
+ (JSC::sizeFrameForForwardArguments):
+ (JSC::setupForwardArgumentsFrame):
+ (JSC::setupForwardArgumentsFrameAndSetThis):
+ * interpreter/Interpreter.h:
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ (JSC::JIT::privateCompileSlowCases):
+ * jit/JIT.h:
+ * jit/JITCall.cpp:
+ (JSC::JIT::compileSetupVarargsFrame):
+ (JSC::JIT::compileOpCall):
+ (JSC::JIT::compileOpCallSlowCase):
+ (JSC::JIT::emit_op_tail_call_forward_arguments):
+ (JSC::JIT::emitSlow_op_tail_call_forward_arguments):
+ * jit/JITCall32_64.cpp:
+ (JSC::JIT::emitSlow_op_tail_call_forward_arguments):
+ (JSC::JIT::emit_op_tail_call_forward_arguments):
+ (JSC::JIT::compileSetupVarargsFrame):
+ (JSC::JIT::compileOpCall):
+ * jit/JITOperations.cpp:
+ * jit/JITOperations.h:
+ * llint/LLIntSlowPaths.cpp:
+ (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+ (JSC::LLInt::varargsSetup):
+ * llint/LLIntSlowPaths.h:
+ * llint/LowLevelInterpreter.asm:
+ * tests/stress/tailCallForwardArguments.js: Added.
+ (putFuncToPrivateName.createBuiltin):
+ (putFuncToPrivateName):
+ (createTailCallForwardingFuncWith):
+ (baz):
+ (baz2):
+ (baz3):
+ (let.bodyText):
+ (baz4):
+ (baz5):
+ (arrayEq):
+
2016-06-13 Yusuke Suzuki <utatane.tea@gmail.com>
Unreviewed, follow up patch for r201964
diff --git a/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h b/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
index 2c39935..07b5958 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
+++ b/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
@@ -43,6 +43,7 @@
macro(argumentCount) \
macro(assert) \
macro(isObject) \
+ macro(tailCallForwardArguments) \
macro(tryGetById) \
macro(putByValDirect) \
macro(toString)
diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.json b/Source/JavaScriptCore/bytecode/BytecodeList.json
index 8e9805a..4753279 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeList.json
+++ b/Source/JavaScriptCore/bytecode/BytecodeList.json
@@ -108,6 +108,7 @@
{ "name" : "op_call_eval", "length" : 9 },
{ "name" : "op_call_varargs", "length" : 9 },
{ "name" : "op_tail_call_varargs", "length" : 9 },
+ { "name" : "op_tail_call_forward_arguments", "length" : 9 },
{ "name" : "op_ret", "length" : 2 },
{ "name" : "op_construct", "length" : 9 },
{ "name" : "op_construct_varargs", "length" : 9 },
diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
index c37a79c..c7fbd6d 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
+++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
@@ -223,7 +223,8 @@
case op_eq:
case op_push_with_scope:
case op_get_by_id_with_this:
- case op_del_by_val: {
+ case op_del_by_val:
+ case op_tail_call_forward_arguments: {
ASSERT(opcodeLengths[opcodeID] > 3);
functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[3].u.operand);
@@ -390,6 +391,7 @@
case op_new_generator_func_exp:
case op_call_varargs:
case op_tail_call_varargs:
+ case op_tail_call_forward_arguments:
case op_construct_varargs:
case op_get_from_scope:
case op_call:
diff --git a/Source/JavaScriptCore/bytecode/CallLinkInfo.h b/Source/JavaScriptCore/bytecode/CallLinkInfo.h
index beeeaa1..f7e6e73 100644
--- a/Source/JavaScriptCore/bytecode/CallLinkInfo.h
+++ b/Source/JavaScriptCore/bytecode/CallLinkInfo.h
@@ -57,7 +57,7 @@
return ConstructVarargs;
if (opcodeID == op_tail_call)
return TailCall;
- ASSERT(opcodeID == op_tail_call_varargs);
+ ASSERT(opcodeID == op_tail_call_varargs || op_tail_call_forward_arguments);
return TailCallVarargs;
}
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index 4c955ff..65e8fab 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1466,7 +1466,8 @@
case op_construct_varargs:
case op_call_varargs:
- case op_tail_call_varargs: {
+ case op_tail_call_varargs:
+ case op_tail_call_forward_arguments: {
int result = (++it)->u.operand;
int callee = (++it)->u.operand;
int thisValue = (++it)->u.operand;
@@ -1474,7 +1475,19 @@
int firstFreeRegister = (++it)->u.operand;
int varArgOffset = (++it)->u.operand;
++it;
- printLocationAndOp(out, exec, location, it, opcode == op_call_varargs ? "call_varargs" : opcode == op_construct_varargs ? "construct_varargs" : "tail_call_varargs");
+ const char* opName;
+ if (opcode == op_call_varargs)
+ opName = "call_varargs";
+ else if (opcode == op_construct_varargs)
+ opName = "construct_varargs";
+ else if (opcode == op_tail_call_varargs)
+ opName = "tail_call_varargs";
+ else if (opcode == op_tail_call_forward_arguments)
+ opName = "tail_call_forward_arguments";
+ else
+ RELEASE_ASSERT_NOT_REACHED();
+
+ printLocationAndOp(out, exec, location, it, opName);
out.printf("%s, %s, %s, %s, %d, %d", registerName(result).data(), registerName(callee).data(), registerName(thisValue).data(), registerName(arguments).data(), firstFreeRegister, varArgOffset);
dumpValueProfiling(out, it, hasPrintedProfiling);
break;
@@ -2055,6 +2068,7 @@
}
case op_call_varargs:
case op_tail_call_varargs:
+ case op_tail_call_forward_arguments:
case op_construct_varargs:
case op_get_by_val: {
int arrayProfileIndex = pc[opLength - 2].u.operand;
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index 66be0a6..399c2e8 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -3115,6 +3115,12 @@
{
return emitCallVarargs(op_construct_varargs, dst, func, thisRegister, arguments, firstFreeRegister, firstVarArgOffset, divot, divotStart, divotEnd);
}
+
+RegisterID* BytecodeGenerator::emitCallForwardArgumentsInTailPosition(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd)
+{
+ ASSERT(m_inTailPosition);
+ return emitCallVarargs(op_tail_call_forward_arguments, dst, func, thisRegister, nullptr, firstFreeRegister, firstVarArgOffset, divot, divotStart, divotEnd);
+}
RegisterID* BytecodeGenerator::emitCallVarargs(OpcodeID opcode, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd)
{
@@ -3130,7 +3136,7 @@
instructions().append(dst->index());
instructions().append(func->index());
instructions().append(thisRegister ? thisRegister->index() : 0);
- instructions().append(arguments->index());
+ instructions().append(arguments ? arguments->index() : 0);
instructions().append(firstFreeRegister->index());
instructions().append(firstVarArgOffset);
instructions().append(arrayProfile);
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
index 16b1f53..f7b6565 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
@@ -577,6 +577,7 @@
RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
RegisterID* emitCallVarargsInTailPosition(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
+ RegisterID* emitCallForwardArgumentsInTailPosition(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
enum PropertyDescriptorOption {
PropertyConfigurable = 1,
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index dfb4167..8abb458 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -888,6 +888,18 @@
return generator.moveToDestinationIfNeeded(dst, generator.emitDirectPutByVal(base.get(), index.get(), value.get()));
}
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_tailCallForwardArguments(BytecodeGenerator& generator, RegisterID* dst)
+{
+ ArgumentListNode* node = m_args->m_listNode;
+ RefPtr<RegisterID> function = generator.emitNode(node);
+ node = node->m_next;
+ RefPtr<RegisterID> thisRegister = generator.emitNode(node);
+ ASSERT(!node->m_next);
+
+ RefPtr<RegisterID> finalDst = generator.finalDestination(dst);
+ return generator.emitCallForwardArgumentsInTailPosition(finalDst.get(), function.get(), thisRegister.get(), generator.newTemporary(), 0, divot(), divotStart(), divotEnd());
+}
+
RegisterID* BytecodeIntrinsicNode::emit_intrinsic_tryGetById(BytecodeGenerator& generator, RegisterID* dst)
{
ArgumentListNode* node = m_args->m_listNode;
@@ -899,8 +911,8 @@
const Identifier& ident = static_cast<StringNode*>(node->m_expr)->value();
ASSERT(!node->m_next);
- RegisterID* finalDest = generator.finalDestination(dst);
- return generator.emitTryGetById(finalDest, base.get(), ident);
+ RefPtr<RegisterID> finalDest = generator.finalDestination(dst);
+ return generator.emitTryGetById(finalDest.get(), base.get(), ident);
}
RegisterID* BytecodeIntrinsicNode::emit_intrinsic_toString(BytecodeGenerator& generator, RegisterID* dst)
diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp
index 9192cf9..fb3a0b2 100644
--- a/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp
@@ -191,7 +191,7 @@
case TailCallVarargs:
case TailCallVarargsInlinedCaller:
escape(node->child1(), node);
- escape(node->child3(), node);
+ escape(node->child2(), node);
break;
case Check:
@@ -612,7 +612,7 @@
case ConstructVarargs:
case TailCallVarargs:
case TailCallVarargsInlinedCaller: {
- Node* candidate = node->child2().node();
+ Node* candidate = node->child3().node();
if (!m_candidates.contains(candidate))
break;
@@ -633,7 +633,7 @@
unsigned firstChild = m_graph.m_varArgChildren.size();
m_graph.m_varArgChildren.append(node->child1());
- m_graph.m_varArgChildren.append(node->child3());
+ m_graph.m_varArgChildren.append(node->child2());
for (Node* argument : arguments)
m_graph.m_varArgChildren.append(Edge(argument));
switch (node->op()) {
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index bb3be17..d4df093 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -828,7 +828,8 @@
switch (opcodeID) {
case op_tail_call:
- case op_tail_call_varargs: {
+ case op_tail_call_varargs:
+ case op_tail_call_forward_arguments: {
if (!inlineCallFrame()) {
prediction = SpecFullTop;
break;
@@ -1229,7 +1230,7 @@
Node* callNode = addCall(result, op, OpInfo(), callTarget, argumentCountIncludingThis, registerOffset, prediction);
if (callNode->op() == TailCall)
return Terminal;
- ASSERT(callNode->op() != TailCallVarargs);
+ ASSERT(callNode->op() != TailCallVarargs && callNode->op() != TailCallForwardVarargs);
return NonTerminal;
}
@@ -1246,7 +1247,7 @@
Node* callNode = addCall(result, op, callOpInfo, callTarget, argumentCountIncludingThis, registerOffset, prediction);
if (callNode->op() == TailCall)
return Terminal;
- ASSERT(callNode->op() != TailCallVarargs);
+ ASSERT(callNode->op() != TailCallVarargs && callNode->op() != TailCallForwardVarargs);
return NonTerminal;
}
@@ -1254,7 +1255,7 @@
{
ASSERT(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_construct_varargs));
ASSERT(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_tail_call_varargs));
-
+
int result = pc[1].u.operand;
int callee = pc[2].u.operand;
int thisReg = pc[3].u.operand;
@@ -1285,16 +1286,19 @@
data->firstVarArgOffset = firstVarArgOffset;
Node* thisChild = get(VirtualRegister(thisReg));
+ Node* argumentsChild = nullptr;
+ if (op != TailCallForwardVarargs)
+ argumentsChild = get(VirtualRegister(arguments));
- if (op == TailCallVarargs) {
+ if (op == TailCallVarargs || op == TailCallForwardVarargs) {
if (allInlineFramesAreTailCalls()) {
- addToGraph(op, OpInfo(data), OpInfo(), callTarget, get(VirtualRegister(arguments)), thisChild);
+ addToGraph(op, OpInfo(data), OpInfo(), callTarget, thisChild, argumentsChild);
return Terminal;
}
- op = TailCallVarargsInlinedCaller;
+ op = op == TailCallVarargs ? TailCallVarargsInlinedCaller : TailCallForwardVarargsInlinedCaller;
}
- Node* call = addToGraph(op, OpInfo(data), OpInfo(prediction), callTarget, get(VirtualRegister(arguments)), thisChild);
+ Node* call = addToGraph(op, OpInfo(data), OpInfo(prediction), callTarget, thisChild, argumentsChild);
VirtualRegister resultReg(result);
if (resultReg.isValid())
set(resultReg, call);
@@ -1767,8 +1771,11 @@
data->offset = argumentsOffset;
data->limit = maxNumArguments;
data->mandatoryMinimum = mandatoryMinimum;
-
- addToGraph(LoadVarargs, OpInfo(data), get(argumentsArgument));
+
+ if (callOp == TailCallForwardVarargs)
+ addToGraph(ForwardVarargs, OpInfo(data));
+ else
+ addToGraph(LoadVarargs, OpInfo(data), get(argumentsArgument));
// LoadVarargs may OSR exit. Hence, we need to keep alive callTargetNode, thisArgument
// and argumentsArgument for the baseline JIT. However, we only need a Phantom for
@@ -1962,7 +1969,7 @@
m_exitOK = true;
processSetLocalQueue(); // This only comes into play for intrinsics, since normal inlined code will leave an empty queue.
if (Node* terminal = m_currentBlock->terminal())
- ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs);
+ ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs || terminal->op() == TailCallForwardVarargs);
else {
addToGraph(Jump);
landingBlocks.append(m_currentBlock);
@@ -2003,7 +2010,7 @@
m_exitOK = true; // Origin changed, so it's fine to exit again.
processSetLocalQueue();
if (Node* terminal = m_currentBlock->terminal())
- ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs);
+ ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs || terminal->op() == TailCallForwardVarargs);
else {
addToGraph(Jump);
landingBlocks.append(m_currentBlock);
@@ -4437,17 +4444,15 @@
case op_call:
handleCall(currentInstruction, Call, CallMode::Regular);
- // Verify that handleCall(), which could have inlined the callee, didn't trash m_currentInstruction.
- ASSERT(m_currentInstruction == currentInstruction);
+ ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleCall, which may have inlined the callee, trashed m_currentInstruction");
NEXT_OPCODE(op_call);
case op_tail_call: {
flushForReturn();
Terminality terminality = handleCall(currentInstruction, TailCall, CallMode::Tail);
- // Verify that handleCall(), which could have inlined the callee, didn't trash m_currentInstruction.
- ASSERT(m_currentInstruction == currentInstruction);
+ ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleCall, which may have inlined the callee, trashed m_currentInstruction");
// If the call is terminal then we should not parse any further bytecodes as the TailCall will exit the function.
- // If the call is not terminal, however, then we want the subsequent op_ret/op_jumpt to update metadata and clean
+ // If the call is not terminal, however, then we want the subsequent op_ret/op_jump to update metadata and clean
// things up.
if (terminality == NonTerminal) {
NEXT_OPCODE(op_tail_call);
@@ -4458,18 +4463,21 @@
case op_construct:
handleCall(currentInstruction, Construct, CallMode::Construct);
+ ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleCall, which may have inlined the callee, trashed m_currentInstruction");
NEXT_OPCODE(op_construct);
case op_call_varargs: {
handleVarargsCall(currentInstruction, CallVarargs, CallMode::Regular);
+ ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleVarargsCall, which may have inlined the callee, trashed m_currentInstruction");
NEXT_OPCODE(op_call_varargs);
}
case op_tail_call_varargs: {
flushForReturn();
Terminality terminality = handleVarargsCall(currentInstruction, TailCallVarargs, CallMode::Tail);
+ ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleVarargsCall, which may have inlined the callee, trashed m_currentInstruction");
// If the call is terminal then we should not parse any further bytecodes as the TailCall will exit the function.
- // If the call is not terminal, however, then we want the subsequent op_ret/op_jumpt to update metadata and clean
+ // If the call is not terminal, however, then we want the subsequent op_ret/op_jump to update metadata and clean
// things up.
if (terminality == NonTerminal) {
NEXT_OPCODE(op_tail_call_varargs);
@@ -4477,9 +4485,27 @@
LAST_OPCODE(op_tail_call_varargs);
}
}
+
+ case op_tail_call_forward_arguments: {
+ // We need to make sure that we don't unbox our arguments here since that won't be
+ // done by the arguments object creation node as that node may not exist.
+ noticeArgumentsUse();
+ flushForReturn();
+ Terminality terminality = handleVarargsCall(currentInstruction, TailCallForwardVarargs, CallMode::Tail);
+ ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleVarargsCall, which may have inlined the callee, trashed m_currentInstruction");
+ // If the call is terminal then we should not parse any further bytecodes as the TailCall will exit the function.
+ // If the call is not terminal, however, then we want the subsequent op_ret/op_jump to update metadata and clean
+ // things up.
+ if (terminality == NonTerminal) {
+ NEXT_OPCODE(op_tail_call);
+ } else {
+ LAST_OPCODE(op_tail_call);
+ }
+ }
case op_construct_varargs: {
handleVarargsCall(currentInstruction, ConstructVarargs, CallMode::Construct);
+ ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleVarargsCall, which may have inlined the callee, trashed m_currentInstruction");
NEXT_OPCODE(op_construct_varargs);
}
diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
index 2267c3e..2512842 100644
--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
@@ -200,6 +200,7 @@
case op_construct:
case op_call_varargs:
case op_tail_call_varargs:
+ case op_tail_call_forward_arguments:
case op_construct_varargs:
case op_create_direct_arguments:
case op_create_scoped_arguments:
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index a36c3a4..8f7aa15 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -1321,7 +1321,6 @@
case PhantomCreateActivation:
case PhantomDirectArguments:
case PhantomClonedArguments:
- case ForwardVarargs:
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case PutHint:
@@ -1529,6 +1528,7 @@
case TailCallForwardVarargs:
case TailCallForwardVarargsInlinedCaller:
case LoadVarargs:
+ case ForwardVarargs:
case ProfileControlFlow:
case NewObject:
case NewArrayBuffer:
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 4f01dcf..946c0bf 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -748,7 +748,51 @@
ASSERT(op() == CreateActivation);
return bitwise_cast<FrozenValue*>(m_opInfo2)->value();
}
-
+
+ bool hasArgumentsChild()
+ {
+ switch (op()) {
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValOutOfBounds:
+ case LoadVarargs:
+ case ForwardVarargs:
+ case CallVarargs:
+ case CallForwardVarargs:
+ case ConstructVarargs:
+ case ConstructForwardVarargs:
+ case TailCallVarargs:
+ case TailCallForwardVarargs:
+ case TailCallVarargsInlinedCaller:
+ case TailCallForwardVarargsInlinedCaller:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ Edge& argumentsChild()
+ {
+ switch (op()) {
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValOutOfBounds:
+ case LoadVarargs:
+ case ForwardVarargs:
+ return child1();
+ case CallVarargs:
+ case CallForwardVarargs:
+ case ConstructVarargs:
+ case ConstructForwardVarargs:
+ case TailCallVarargs:
+ case TailCallForwardVarargs:
+ case TailCallVarargsInlinedCaller:
+ case TailCallForwardVarargsInlinedCaller:
+ return child3();
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return child1();
+ }
+ }
+
bool containsMovHint()
{
switch (op()) {
diff --git a/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h b/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h
index 3e5a833..8d1a79d 100644
--- a/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h
@@ -115,7 +115,12 @@
case ConstructForwardVarargs:
case TailCallForwardVarargs:
case TailCallForwardVarargsInlinedCaller: {
- InlineCallFrame* inlineCallFrame = m_node->child1()->origin.semantic.inlineCallFrame;
+
+ InlineCallFrame* inlineCallFrame;
+ if (m_node->argumentsChild())
+ inlineCallFrame = m_node->argumentsChild()->origin.semantic.inlineCallFrame;
+ else
+ inlineCallFrame = m_node->origin.semantic.inlineCallFrame;
if (!inlineCallFrame) {
// Read the outermost arguments and argument count.
for (unsigned i = m_graph.m_codeBlock->numParameters(); i-- > 1;)
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 30c3f83..2d58b16 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -972,7 +972,6 @@
case PhantomClonedArguments:
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
- case ForwardVarargs:
case PutHint:
case CheckStructureImmediate:
case MaterializeNewObject:
@@ -1055,6 +1054,7 @@
case ZombieHint:
case ExitOK:
case LoadVarargs:
+ case ForwardVarargs:
case CopyRest:
case PutDynamicVar:
break;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 4cb68e2..c290cdb 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -5952,8 +5952,12 @@
void SpeculativeJIT::compileForwardVarargs(Node* node)
{
LoadVarargsData* data = node->loadVarargsData();
- InlineCallFrame* inlineCallFrame = node->child1()->origin.semantic.inlineCallFrame;
-
+ InlineCallFrame* inlineCallFrame;
+ if (node->child1())
+ inlineCallFrame = node->child1()->origin.semantic.inlineCallFrame;
+ else
+ inlineCallFrame = node->origin.semantic.inlineCallFrame;
+
GPRTemporary length(this);
JSValueRegsTemporary temp(this);
GPRReg lengthGPR = length.gpr();
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 8498bf0..dd3a38a 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -729,7 +729,8 @@
if (isForwardVarargs) {
flushRegisters();
- use(node->child2());
+ if (node->child3())
+ use(node->child3());
GPRReg scratchGPR1;
GPRReg scratchGPR2;
@@ -741,7 +742,12 @@
m_jit.move(TrustedImm32(numUsedStackSlots), scratchGPR2);
JITCompiler::JumpList slowCase;
- emitSetupVarargsFrameFastCase(m_jit, scratchGPR2, scratchGPR1, scratchGPR2, scratchGPR3, node->child2()->origin.semantic.inlineCallFrame, data->firstVarArgOffset, slowCase);
+ InlineCallFrame* inlineCallFrame;
+ if (node->child3())
+ inlineCallFrame = node->child3()->origin.semantic.inlineCallFrame;
+ else
+ inlineCallFrame = node->origin.semantic.inlineCallFrame;
+ emitSetupVarargsFrameFastCase(m_jit, scratchGPR2, scratchGPR1, scratchGPR2, scratchGPR3, inlineCallFrame, data->firstVarArgOffset, slowCase);
JITCompiler::Jump done = m_jit.jump();
slowCase.link(&m_jit);
callOperation(operationThrowStackOverflowForVarargs);
@@ -759,7 +765,7 @@
auto loadArgumentsGPR = [&] (GPRReg reservedGPR) {
if (reservedGPR != InvalidGPRReg)
lock(reservedGPR);
- JSValueOperand arguments(this, node->child2());
+ JSValueOperand arguments(this, node->child3());
argumentsTagGPR = arguments.tagGPR();
argumentsPayloadGPR = arguments.payloadGPR();
if (reservedGPR != InvalidGPRReg)
@@ -798,7 +804,7 @@
// We don't need the arguments array anymore.
if (isVarargs)
- use(node->child2());
+ use(node->child3());
// Now set up the "this" argument.
JSValueOperand thisArgument(this, node->child3());
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 1d4fa76..130e5c5 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -701,7 +701,8 @@
if (isForwardVarargs) {
flushRegisters();
- use(node->child2());
+ if (node->child3())
+ use(node->child3());
GPRReg scratchGPR1;
GPRReg scratchGPR2;
@@ -713,7 +714,12 @@
m_jit.move(TrustedImm32(numUsedStackSlots), scratchGPR2);
JITCompiler::JumpList slowCase;
- emitSetupVarargsFrameFastCase(m_jit, scratchGPR2, scratchGPR1, scratchGPR2, scratchGPR3, node->child2()->origin.semantic.inlineCallFrame, data->firstVarArgOffset, slowCase);
+ InlineCallFrame* inlineCallFrame;
+ if (node->child3())
+ inlineCallFrame = node->child3()->origin.semantic.inlineCallFrame;
+ else
+ inlineCallFrame = node->origin.semantic.inlineCallFrame;
+ emitSetupVarargsFrameFastCase(m_jit, scratchGPR2, scratchGPR1, scratchGPR2, scratchGPR3, inlineCallFrame, data->firstVarArgOffset, slowCase);
JITCompiler::Jump done = m_jit.jump();
slowCase.link(&m_jit);
callOperation(operationThrowStackOverflowForVarargs);
@@ -730,7 +736,7 @@
auto loadArgumentsGPR = [&] (GPRReg reservedGPR) {
if (reservedGPR != InvalidGPRReg)
lock(reservedGPR);
- JSValueOperand arguments(this, node->child2());
+ JSValueOperand arguments(this, node->child3());
argumentsGPR = arguments.gpr();
if (reservedGPR != InvalidGPRReg)
unlock(reservedGPR);
@@ -767,10 +773,10 @@
// We don't need the arguments array anymore.
if (isVarargs)
- use(node->child2());
+ use(node->child3());
// Now set up the "this" argument.
- JSValueOperand thisArgument(this, node->child3());
+ JSValueOperand thisArgument(this, node->child2());
GPRReg thisArgumentGPR = thisArgument.gpr();
thisArgument.use();
diff --git a/Source/JavaScriptCore/dfg/DFGVarargsForwardingPhase.cpp b/Source/JavaScriptCore/dfg/DFGVarargsForwardingPhase.cpp
index 535e79f..e3c52ac 100644
--- a/Source/JavaScriptCore/dfg/DFGVarargsForwardingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGVarargsForwardingPhase.cpp
@@ -137,7 +137,7 @@
case ConstructVarargs:
case TailCallVarargs:
case TailCallVarargsInlinedCaller:
- if (node->child1() == candidate || node->child3() == candidate) {
+ if (node->child1() == candidate || node->child2() == candidate) {
if (verbose)
dataLog(" Escape at ", node, "\n");
return;
@@ -274,25 +274,25 @@
break;
case CallVarargs:
- if (node->child2() != candidate)
+ if (node->child3() != candidate)
break;
node->setOpAndDefaultFlags(CallForwardVarargs);
break;
case ConstructVarargs:
- if (node->child2() != candidate)
+ if (node->child3() != candidate)
break;
node->setOpAndDefaultFlags(ConstructForwardVarargs);
break;
case TailCallVarargs:
- if (node->child2() != candidate)
+ if (node->child3() != candidate)
break;
node->setOpAndDefaultFlags(TailCallForwardVarargs);
break;
case TailCallVarargsInlinedCaller:
- if (node->child2() != candidate)
+ if (node->child3() != candidate)
break;
node->setOpAndDefaultFlags(TailCallForwardVarargsInlinedCaller);
break;
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
index 78531b1..a483c4d 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
@@ -5197,7 +5197,7 @@
{
Node* node = m_node;
LValue jsCallee = lowJSValue(m_node->child1());
- LValue thisArg = lowJSValue(m_node->child3());
+ LValue thisArg = lowJSValue(m_node->child2());
LValue jsArguments = nullptr;
bool forwarding = false;
@@ -5207,7 +5207,7 @@
case TailCallVarargs:
case TailCallVarargsInlinedCaller:
case ConstructVarargs:
- jsArguments = lowJSValue(node->child2());
+ jsArguments = lowJSValue(node->child3());
break;
case CallForwardVarargs:
case TailCallForwardVarargs:
@@ -5360,7 +5360,12 @@
jit.move(CCallHelpers::TrustedImm32(originalStackHeight / sizeof(EncodedJSValue)), scratchGPR2);
CCallHelpers::JumpList slowCase;
- emitSetupVarargsFrameFastCase(jit, scratchGPR2, scratchGPR1, scratchGPR2, scratchGPR3, node->child2()->origin.semantic.inlineCallFrame, data->firstVarArgOffset, slowCase);
+ InlineCallFrame* inlineCallFrame;
+ if (node->child3())
+ inlineCallFrame = node->child3()->origin.semantic.inlineCallFrame;
+ else
+ inlineCallFrame = node->origin.semantic.inlineCallFrame;
+ emitSetupVarargsFrameFastCase(jit, scratchGPR2, scratchGPR1, scratchGPR2, scratchGPR3, inlineCallFrame, data->firstVarArgOffset, slowCase);
CCallHelpers::Jump done = jit.jump();
slowCase.link(&jit);
@@ -5504,7 +5509,11 @@
void compileForwardVarargs()
{
LoadVarargsData* data = m_node->loadVarargsData();
- InlineCallFrame* inlineCallFrame = m_node->child1()->origin.semantic.inlineCallFrame;
+ InlineCallFrame* inlineCallFrame;
+ if (m_node->child1())
+ inlineCallFrame = m_node->child1()->origin.semantic.inlineCallFrame;
+ else
+ inlineCallFrame = m_node->origin.semantic.inlineCallFrame;
LValue length = getArgumentsLength(inlineCallFrame).value;
LValue lengthIncludingThis = m_out.add(length, m_out.constInt32(1 - data->offset));
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
index c111d00..792a45e 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -235,6 +235,16 @@
return length;
}
+unsigned sizeFrameForForwardArguments(CallFrame* callFrame, JSStack* stack, unsigned numUsedStackSlots)
+{
+ unsigned length = callFrame->argumentCount();
+ CallFrame* calleeFrame = calleeFrameForVarargs(callFrame, numUsedStackSlots, length + 1);
+ if (!stack->ensureCapacityFor(calleeFrame->registers()))
+ throwStackOverflowError(callFrame);
+
+ return length;
+}
+
unsigned sizeFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset)
{
unsigned length = sizeOfVarargs(callFrame, arguments, firstVarArgOffset);
@@ -295,6 +305,22 @@
newCallFrame->setThisValue(thisValue);
}
+void setupForwardArgumentsFrame(CallFrame* execCaller, CallFrame* execCallee, uint32_t length)
+{
+ ASSERT(length == execCaller->argumentCount());
+ unsigned offset = execCaller->argumentOffset(0) * sizeof(Register);
+ memcpy(reinterpret_cast<char*>(execCallee) + offset, reinterpret_cast<char*>(execCaller) + offset, length * sizeof(Register));
+ execCallee->setArgumentCountIncludingThis(length + 1);
+}
+
+void setupForwardArgumentsFrameAndSetThis(CallFrame* execCaller, CallFrame* execCallee, JSValue thisValue, uint32_t length)
+{
+ setupForwardArgumentsFrame(execCaller, execCallee, length);
+ execCallee->setThisValue(thisValue);
+}
+
+
+
Interpreter::Interpreter(VM& vm)
: m_vm(vm)
, m_stack(vm)
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h
index 29c836c..6f72082 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.h
+++ b/Source/JavaScriptCore/interpreter/Interpreter.h
@@ -274,9 +274,12 @@
unsigned sizeOfVarargs(CallFrame* exec, JSValue arguments, uint32_t firstVarArgOffset);
static const unsigned maxArguments = 0x10000;
unsigned sizeFrameForVarargs(CallFrame* exec, JSStack*, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset);
+ unsigned sizeFrameForForwardArguments(CallFrame* exec, JSStack*, unsigned numUsedStackSlots);
void loadVarargs(CallFrame* execCaller, VirtualRegister firstElementDest, JSValue source, uint32_t offset, uint32_t length);
void setupVarargsFrame(CallFrame* execCaller, CallFrame* execCallee, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length);
void setupVarargsFrameAndSetThis(CallFrame* execCaller, CallFrame* execCallee, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length);
+ void setupForwardArgumentsFrame(CallFrame* execCaller, CallFrame* execCallee, uint32_t length);
+ void setupForwardArgumentsFrameAndSetThis(CallFrame* execCaller, CallFrame* execCallee, JSValue thisValue, uint32_t length);
} // namespace JSC
diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp
index 95b1ac1..3de2e45 100644
--- a/Source/JavaScriptCore/jit/JIT.cpp
+++ b/Source/JavaScriptCore/jit/JIT.cpp
@@ -215,6 +215,7 @@
DEFINE_OP(op_call_eval)
DEFINE_OP(op_call_varargs)
DEFINE_OP(op_tail_call_varargs)
+ DEFINE_OP(op_tail_call_forward_arguments)
DEFINE_OP(op_construct_varargs)
DEFINE_OP(op_catch)
DEFINE_OP(op_construct)
@@ -415,6 +416,7 @@
DEFINE_SLOWCASE_OP(op_call_eval)
DEFINE_SLOWCASE_OP(op_call_varargs)
DEFINE_SLOWCASE_OP(op_tail_call_varargs)
+ DEFINE_SLOWCASE_OP(op_tail_call_forward_arguments)
DEFINE_SLOWCASE_OP(op_construct_varargs)
DEFINE_SLOWCASE_OP(op_construct)
DEFINE_SLOWCASE_OP(op_to_this)
diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h
index 82e831e..f546585 100644
--- a/Source/JavaScriptCore/jit/JIT.h
+++ b/Source/JavaScriptCore/jit/JIT.h
@@ -317,7 +317,7 @@
void compileOpCall(OpcodeID, Instruction*, unsigned callLinkInfoIndex);
void compileOpCallSlowCase(OpcodeID, Instruction*, Vector<SlowCaseEntry>::iterator&, unsigned callLinkInfoIndex);
- void compileSetupVarargsFrame(Instruction*, CallLinkInfo*);
+ void compileSetupVarargsFrame(OpcodeID, Instruction*, CallLinkInfo*);
void compileCallEval(Instruction*);
void compileCallEvalSlowCase(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitPutCallResult(Instruction*);
@@ -483,6 +483,7 @@
void emit_op_call_eval(Instruction*);
void emit_op_call_varargs(Instruction*);
void emit_op_tail_call_varargs(Instruction*);
+ void emit_op_tail_call_forward_arguments(Instruction*);
void emit_op_construct_varargs(Instruction*);
void emit_op_catch(Instruction*);
void emit_op_construct(Instruction*);
@@ -614,6 +615,7 @@
void emitSlow_op_call_eval(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_call_varargs(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_tail_call_varargs(Instruction*, Vector<SlowCaseEntry>::iterator&);
+ void emitSlow_op_tail_call_forward_arguments(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_construct_varargs(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_construct(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_to_this(Instruction*, Vector<SlowCaseEntry>::iterator&);
diff --git a/Source/JavaScriptCore/jit/JITCall.cpp b/Source/JavaScriptCore/jit/JITCall.cpp
index 599f86c..7edfcc0 100644
--- a/Source/JavaScriptCore/jit/JITCall.cpp
+++ b/Source/JavaScriptCore/jit/JITCall.cpp
@@ -53,7 +53,7 @@
emitPutVirtualRegister(dst);
}
-void JIT::compileSetupVarargsFrame(Instruction* instruction, CallLinkInfo* info)
+void JIT::compileSetupVarargsFrame(OpcodeID opcode, Instruction* instruction, CallLinkInfo* info)
{
int thisValue = instruction[3].u.operand;
int arguments = instruction[4].u.operand;
@@ -61,12 +61,22 @@
int firstVarArgOffset = instruction[6].u.operand;
emitGetVirtualRegister(arguments, regT1);
- callOperation(operationSizeFrameForVarargs, regT1, -firstFreeRegister, firstVarArgOffset);
+ Z_JITOperation_EJZZ sizeOperation;
+ if (opcode == op_tail_call_forward_arguments)
+ sizeOperation = operationSizeFrameForForwardArguments;
+ else
+ sizeOperation = operationSizeFrameForVarargs;
+ callOperation(sizeOperation, regT1, -firstFreeRegister, firstVarArgOffset);
move(TrustedImm32(-firstFreeRegister), regT1);
emitSetVarargsFrame(*this, returnValueGPR, false, regT1, regT1);
addPtr(TrustedImm32(-(sizeof(CallerFrameAndPC) + WTF::roundUpToMultipleOf(stackAlignmentBytes(), 5 * sizeof(void*)))), regT1, stackPointerRegister);
emitGetVirtualRegister(arguments, regT2);
- callOperation(operationSetupVarargsFrame, regT1, regT2, firstVarArgOffset, regT0);
+ F_JITOperation_EFJZZ setupOperation;
+ if (opcode == op_tail_call_forward_arguments)
+ setupOperation = operationSetupForwardArgumentsFrame;
+ else
+ setupOperation = operationSetupVarargsFrame;
+ callOperation(setupOperation, regT1, regT2, firstVarArgOffset, regT0);
move(returnValueGPR, regT1);
// Profile the argument count.
@@ -147,11 +157,13 @@
COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct_varargs), call_and_construct_varargs_opcodes_must_be_same_length);
COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call), call_and_tail_call_opcodes_must_be_same_length);
COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call_varargs), call_and_tail_call_varargs_opcodes_must_be_same_length);
+ COMPILE_ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call_forward_arguments), call_and_tail_call_forward_arguments_opcodes_must_be_same_length);
+
CallLinkInfo* info;
if (opcodeID != op_call_eval)
info = m_codeBlock->addCallLinkInfo();
- if (opcodeID == op_call_varargs || opcodeID == op_construct_varargs || opcodeID == op_tail_call_varargs)
- compileSetupVarargsFrame(instruction, info);
+ if (opcodeID == op_call_varargs || opcodeID == op_construct_varargs || opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments)
+ compileSetupVarargsFrame(opcodeID, instruction, info);
else {
int argCount = instruction[3].u.operand;
int registerOffset = -instruction[4].u.operand;
@@ -171,8 +183,8 @@
uint32_t bytecodeOffset = instruction - m_codeBlock->instructions().begin();
uint32_t locationBits = CallSiteIndex(bytecodeOffset).bits();
store32(TrustedImm32(locationBits), Address(callFrameRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + TagOffset));
- emitGetVirtualRegister(callee, regT0); // regT0 holds callee.
+ emitGetVirtualRegister(callee, regT0); // regT0 holds callee.
store64(regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) - sizeof(CallerFrameAndPC)));
if (opcodeID == op_call_eval) {
@@ -211,7 +223,7 @@
return;
}
- if (opcodeID == op_tail_call_varargs) {
+ if (opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments) {
emitRestoreCalleeSaves();
prepareForTailCallSlow();
m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedTailCall();
@@ -237,7 +249,7 @@
linkSlowCase(iter);
- if (opcodeID == op_tail_call || opcodeID == op_tail_call_varargs)
+ if (opcodeID == op_tail_call || opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments)
emitRestoreCalleeSaves();
move(TrustedImmPtr(m_callCompilationInfo[callLinkInfoIndex].callLinkInfo), regT2);
@@ -282,6 +294,11 @@
compileOpCall(op_tail_call_varargs, currentInstruction, m_callLinkInfoIndex++);
}
+void JIT::emit_op_tail_call_forward_arguments(Instruction* currentInstruction)
+{
+ compileOpCall(op_tail_call_forward_arguments, currentInstruction, m_callLinkInfoIndex++);
+}
+
void JIT::emit_op_construct_varargs(Instruction* currentInstruction)
{
compileOpCall(op_construct_varargs, currentInstruction, m_callLinkInfoIndex++);
@@ -316,6 +333,11 @@
{
compileOpCallSlowCase(op_tail_call_varargs, currentInstruction, iter, m_callLinkInfoIndex++);
}
+
+void JIT::emitSlow_op_tail_call_forward_arguments(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ compileOpCallSlowCase(op_tail_call_forward_arguments, currentInstruction, iter, m_callLinkInfoIndex++);
+}
void JIT::emitSlow_op_construct_varargs(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
diff --git a/Source/JavaScriptCore/jit/JITCall32_64.cpp b/Source/JavaScriptCore/jit/JITCall32_64.cpp
index 8e9b239..bba306a 100644
--- a/Source/JavaScriptCore/jit/JITCall32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITCall32_64.cpp
@@ -87,6 +87,11 @@
{
compileOpCallSlowCase(op_tail_call_varargs, currentInstruction, iter, m_callLinkInfoIndex++);
}
+
+void JIT::emitSlow_op_tail_call_forward_arguments(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ compileOpCallSlowCase(op_tail_call_forward_arguments, currentInstruction, iter, m_callLinkInfoIndex++);
+}
void JIT::emitSlow_op_construct_varargs(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
@@ -122,6 +127,11 @@
{
compileOpCall(op_tail_call_varargs, currentInstruction, m_callLinkInfoIndex++);
}
+
+void JIT::emit_op_tail_call_forward_arguments(Instruction* currentInstruction)
+{
+ compileOpCall(op_tail_call_forward_arguments, currentInstruction, m_callLinkInfoIndex++);
+}
void JIT::emit_op_construct_varargs(Instruction* currentInstruction)
{
@@ -133,7 +143,7 @@
compileOpCall(op_construct, currentInstruction, m_callLinkInfoIndex++);
}
-void JIT::compileSetupVarargsFrame(Instruction* instruction, CallLinkInfo* info)
+void JIT::compileSetupVarargsFrame(OpcodeID opcode, Instruction* instruction, CallLinkInfo* info)
{
int thisValue = instruction[3].u.operand;
int arguments = instruction[4].u.operand;
@@ -141,12 +151,22 @@
int firstVarArgOffset = instruction[6].u.operand;
emitLoad(arguments, regT1, regT0);
- callOperation(operationSizeFrameForVarargs, regT1, regT0, -firstFreeRegister, firstVarArgOffset);
+ Z_JITOperation_EJZZ sizeOperation;
+ if (opcode == op_tail_call_forward_arguments)
+ sizeOperation = operationSizeFrameForForwardArguments;
+ else
+ sizeOperation = operationSizeFrameForVarargs;
+ callOperation(sizeOperation, regT1, regT0, -firstFreeRegister, firstVarArgOffset);
move(TrustedImm32(-firstFreeRegister), regT1);
emitSetVarargsFrame(*this, returnValueGPR, false, regT1, regT1);
addPtr(TrustedImm32(-(sizeof(CallerFrameAndPC) + WTF::roundUpToMultipleOf(stackAlignmentBytes(), 6 * sizeof(void*)))), regT1, stackPointerRegister);
emitLoad(arguments, regT2, regT4);
- callOperation(operationSetupVarargsFrame, regT1, regT2, regT4, firstVarArgOffset, regT0);
+ F_JITOperation_EFJZZ setupOperation;
+ if (opcode == op_tail_call_forward_arguments)
+ setupOperation = operationSetupForwardArgumentsFrame;
+ else
+ setupOperation = operationSetupVarargsFrame;
+ callOperation(setupOperation, regT1, regT2, regT4, firstVarArgOffset, regT0);
move(returnValueGPR, regT1);
// Profile the argument count.
@@ -229,8 +249,8 @@
CallLinkInfo* info = nullptr;
if (opcodeID != op_call_eval)
info = m_codeBlock->addCallLinkInfo();
- if (opcodeID == op_call_varargs || opcodeID == op_construct_varargs || opcodeID == op_tail_call_varargs)
- compileSetupVarargsFrame(instruction, info);
+ if (opcodeID == op_call_varargs || opcodeID == op_construct_varargs || opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments)
+ compileSetupVarargsFrame(opcodeID, instruction, info);
else {
int argCount = instruction[3].u.operand;
int registerOffset = -instruction[4].u.operand;
@@ -277,7 +297,7 @@
m_callCompilationInfo[callLinkInfoIndex].callLinkInfo = info;
checkStackPointerAlignment();
- if (opcodeID == op_tail_call || opcodeID == op_tail_call_varargs) {
+ if (opcodeID == op_tail_call || opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments) {
prepareForTailCallSlow();
m_callCompilationInfo[callLinkInfoIndex].hotPathOther = emitNakedTailCall();
return;
diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp
index 1745d6e..7a9948a 100644
--- a/Source/JavaScriptCore/jit/JITOperations.cpp
+++ b/Source/JavaScriptCore/jit/JITOperations.cpp
@@ -1917,6 +1917,14 @@
return JSValue::encode(jsBoolean(result));
}
+int32_t JIT_OPERATION operationSizeFrameForForwardArguments(ExecState* exec, EncodedJSValue, int32_t numUsedStackSlots, int32_t)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ JSStack* stack = &exec->interpreter()->stack();
+ return sizeFrameForForwardArguments(exec, stack, numUsedStackSlots);
+}
+
int32_t JIT_OPERATION operationSizeFrameForVarargs(ExecState* exec, EncodedJSValue encodedArguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset)
{
VM& vm = exec->vm();
@@ -1926,6 +1934,14 @@
return sizeFrameForVarargs(exec, stack, arguments, numUsedStackSlots, firstVarArgOffset);
}
+CallFrame* JIT_OPERATION operationSetupForwardArgumentsFrame(ExecState* exec, CallFrame* newCallFrame, EncodedJSValue, int32_t, int32_t length)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ setupForwardArgumentsFrame(exec, newCallFrame, length);
+ return newCallFrame;
+}
+
CallFrame* JIT_OPERATION operationSetupVarargsFrame(ExecState* exec, CallFrame* newCallFrame, EncodedJSValue encodedArguments, int32_t firstVarArgOffset, int32_t length)
{
VM& vm = exec->vm();
diff --git a/Source/JavaScriptCore/jit/JITOperations.h b/Source/JavaScriptCore/jit/JITOperations.h
index a9726eb..14fa038 100644
--- a/Source/JavaScriptCore/jit/JITOperations.h
+++ b/Source/JavaScriptCore/jit/JITOperations.h
@@ -380,7 +380,9 @@
size_t JIT_OPERATION operationDeleteByVal(ExecState*, EncodedJSValue base, EncodedJSValue target) WTF_INTERNAL;
JSCell* JIT_OPERATION operationGetPNames(ExecState*, JSObject*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationInstanceOf(ExecState*, EncodedJSValue, EncodedJSValue proto) WTF_INTERNAL;
+int32_t JIT_OPERATION operationSizeFrameForForwardArguments(ExecState*, EncodedJSValue arguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset) WTF_INTERNAL;
int32_t JIT_OPERATION operationSizeFrameForVarargs(ExecState*, EncodedJSValue arguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset) WTF_INTERNAL;
+CallFrame* JIT_OPERATION operationSetupForwardArgumentsFrame(ExecState*, CallFrame*, EncodedJSValue, int32_t, int32_t length) WTF_INTERNAL;
CallFrame* JIT_OPERATION operationSetupVarargsFrame(ExecState*, CallFrame*, EncodedJSValue arguments, int32_t firstVarArgOffset, int32_t length) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationToObject(ExecState*, EncodedJSValue) WTF_INTERNAL;
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
index 67304ce..41cde57 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
@@ -1364,47 +1364,69 @@
LLINT_RETURN_CALLEE_FRAME(execCallee);
}
-LLINT_SLOW_PATH_DECL(slow_path_call_varargs)
+LLINT_SLOW_PATH_DECL(slow_path_size_frame_for_forward_arguments)
+{
+ LLINT_BEGIN();
+ // This needs to:
+ // - Set up a call frame with the same arguments as the current frame.
+
+ unsigned numUsedStackSlots = -pc[5].u.operand;
+
+ unsigned arguments = sizeFrameForForwardArguments(exec, &vm.interpreter->stack(), numUsedStackSlots);
+ LLINT_CALL_CHECK_EXCEPTION(exec, exec);
+
+ ExecState* execCallee = calleeFrameForVarargs(exec, numUsedStackSlots, arguments + 1);
+
+ vm.varargsLength = arguments;
+ vm.newCallFrameReturnValue = execCallee;
+
+ LLINT_RETURN_CALLEE_FRAME(execCallee);
+}
+
+enum class SetArgumentsWith {
+ Object,
+ CurrentArguments
+};
+
+inline SlowPathReturnType varargsSetup(ExecState* exec, Instruction* pc, CodeSpecializationKind kind, SetArgumentsWith set)
{
LLINT_BEGIN_NO_SET_PC();
// This needs to:
// - Figure out what to call and compile it if necessary.
// - Return a tuple of machine code address to call and the new call frame.
-
+
JSValue calleeAsValue = LLINT_OP_C(2).jsValue();
-
+
ExecState* execCallee = vm.newCallFrameReturnValue;
- setupVarargsFrameAndSetThis(exec, execCallee, LLINT_OP_C(3).jsValue(), LLINT_OP_C(4).jsValue(), pc[6].u.operand, vm.varargsLength);
- LLINT_CALL_CHECK_EXCEPTION(exec, exec);
-
- execCallee->uncheckedR(JSStack::Callee) = calleeAsValue;
+ if (set == SetArgumentsWith::Object) {
+ setupVarargsFrameAndSetThis(exec, execCallee, LLINT_OP_C(3).jsValue(), LLINT_OP_C(4).jsValue(), pc[6].u.operand, vm.varargsLength);
+ LLINT_CALL_CHECK_EXCEPTION(exec, exec);
+ } else
+ setupForwardArgumentsFrameAndSetThis(exec, execCallee, LLINT_OP_C(3).jsValue(), vm.varargsLength);
+
execCallee->setCallerFrame(exec);
+ execCallee->uncheckedR(JSStack::Callee) = calleeAsValue;
exec->setCurrentVPC(pc);
-
- return setUpCall(execCallee, pc, CodeForCall, calleeAsValue);
+
+ return setUpCall(execCallee, pc, kind, calleeAsValue);
}
-
+
+LLINT_SLOW_PATH_DECL(slow_path_call_varargs)
+{
+ return varargsSetup(exec, pc, CodeForCall, SetArgumentsWith::Object);
+}
+
+LLINT_SLOW_PATH_DECL(slow_path_tail_call_forward_arguments)
+{
+ return varargsSetup(exec, pc, CodeForCall, SetArgumentsWith::CurrentArguments);
+}
+
LLINT_SLOW_PATH_DECL(slow_path_construct_varargs)
{
- LLINT_BEGIN_NO_SET_PC();
- // This needs to:
- // - Figure out what to call and compile it if necessary.
- // - Return a tuple of machine code address to call and the new call frame.
-
- JSValue calleeAsValue = LLINT_OP_C(2).jsValue();
-
- ExecState* execCallee = vm.newCallFrameReturnValue;
-
- setupVarargsFrameAndSetThis(exec, execCallee, LLINT_OP_C(3).jsValue(), LLINT_OP_C(4).jsValue(), pc[6].u.operand, vm.varargsLength);
- LLINT_CALL_CHECK_EXCEPTION(exec, exec);
-
- execCallee->uncheckedR(JSStack::Callee) = calleeAsValue;
- execCallee->setCallerFrame(exec);
- exec->setCurrentVPC(pc);
-
- return setUpCall(execCallee, pc, CodeForConstruct, calleeAsValue);
+ return varargsSetup(exec, pc, CodeForConstruct, SetArgumentsWith::Object);
}
+
LLINT_SLOW_PATH_DECL(slow_path_call_eval)
{
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.h b/Source/JavaScriptCore/llint/LLIntSlowPaths.h
index 64ec592..dd2c4ca 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.h
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.h
@@ -107,7 +107,9 @@
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_call);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_construct);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_size_frame_for_varargs);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_size_frame_for_forward_arguments);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_call_varargs);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_tail_call_forward_arguments);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_construct_varargs);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_call_eval);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_tear_off_arguments);
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
index 9412681..d6db57f 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
@@ -1529,8 +1529,8 @@
traceExecution()
doCall(_llint_slow_path_construct, prepareForRegularCall)
-macro doCallVarargs(slowPath, prepareCall)
- callSlowPath(_llint_slow_path_size_frame_for_varargs)
+macro doCallVarargs(frameSlowPath, slowPath, prepareCall)
+ callSlowPath(frameSlowPath)
branchIfException(_llint_throw_from_slow_path_trampoline)
# calleeFrame in r1
if JSVALUE64
@@ -1549,18 +1549,27 @@
_llint_op_call_varargs:
traceExecution()
- doCallVarargs(_llint_slow_path_call_varargs, prepareForRegularCall)
+ doCallVarargs(_llint_slow_path_size_frame_for_varargs, _llint_slow_path_call_varargs, prepareForRegularCall)
_llint_op_tail_call_varargs:
traceExecution()
checkSwitchToJITForEpilogue()
# We lie and perform the tail call instead of preparing it since we can't
# prepare the frame for a call opcode
- doCallVarargs(_llint_slow_path_call_varargs, prepareForTailCall)
+ doCallVarargs(_llint_slow_path_size_frame_for_varargs, _llint_slow_path_call_varargs, prepareForTailCall)
+
+
+_llint_op_tail_call_forward_arguments:
+ traceExecution()
+ checkSwitchToJITForEpilogue()
+ # We lie and perform the tail call instead of preparing it since we can't
+ # prepare the frame for a call opcode
+ doCallVarargs(_llint_slow_path_size_frame_for_forward_arguments, _llint_slow_path_tail_call_forward_arguments, prepareForTailCall)
+
_llint_op_construct_varargs:
traceExecution()
- doCallVarargs(_llint_slow_path_construct_varargs, prepareForRegularCall)
+ doCallVarargs(_llint_slow_path_size_frame_for_varargs, _llint_slow_path_construct_varargs, prepareForRegularCall)
_llint_op_call_eval:
diff --git a/Source/JavaScriptCore/tests/stress/tailCallForwardArguments.js b/Source/JavaScriptCore/tests/stress/tailCallForwardArguments.js
new file mode 100644
index 0000000..fece9d6
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/tailCallForwardArguments.js
@@ -0,0 +1,161 @@
+// This is pretty bad but I need a private name.
+var putFuncToPrivateName = createBuiltin(`(function(func) { @arrayIteratorIsDone = func })`)
+putFuncToPrivateName(function (a,b) { return b; })
+
+function createTailCallForwardingFuncWith(body, thisValue) {
+ return createBuiltin(`(function (a) {
+ "use strict";
+
+ ${body}
+
+ return @tailCallForwardArguments(@arrayIteratorIsDone, ${thisValue});
+ })`);
+}
+
+var foo = createTailCallForwardingFuncWith("", "@undefined");
+
+function baz() {
+ return foo.call(true, 7);
+}
+noInline(baz);
+
+
+
+var fooNoInline = createTailCallForwardingFuncWith("", "@undefined");
+noInline(foo);
+
+for (let i = 0; i < 100000; i++) {
+ if (baz.call() !== undefined)
+ throw new Error(i);
+ if (fooNoInline.call(undefined, 3) !== undefined)
+ throw new Error(i);
+}
+
+putFuncToPrivateName(function () { "use strict"; return { thisValue: this, argumentsValue: arguments}; });
+var foo2 = createTailCallForwardingFuncWith("", "this");
+var fooNI2 = createTailCallForwardingFuncWith("", "this");
+noInline(fooNI2);
+
+function baz2() {
+ return foo2.call(true, 7);
+}
+noInline(baz2);
+
+for (let i = 0; i < 100000; i++) {
+ let result = foo2.call(true, 7);
+ if (result.thisValue !== true || result.argumentsValue.length !== 1 || result.argumentsValue[0] !== 7)
+ throw new Error(i);
+ result = baz2.call();
+ if (result.thisValue !== true || result.argumentsValue.length !== 1 || result.argumentsValue[0] !== 7)
+ throw new Error(i);
+ result = fooNI2.call(true, 7);
+ if (result.thisValue !== true || result.argumentsValue.length !== 1 || result.argumentsValue[0] !== 7)
+ throw new Error(i);
+}
+
+putFuncToPrivateName(function () { "use strict"; return this; });
+var foo3 = createTailCallForwardingFuncWith("", "{ thisValue: this, otherValue: 'hello'} ");
+var fooNI3 = createTailCallForwardingFuncWith("", "{ thisValue: this, otherValue: 'hello'} ");
+noInline(fooNI3);
+function baz3() {
+ return foo3.call(true, 7);
+}
+noInline(baz3);
+
+for (let i = 0; i < 100000; i++) {
+ let result = foo3.call(true, 7);
+ if (result.thisValue !== true)
+ throw new Error(i);
+ result = baz3.call();
+ if (result.thisValue !== true)
+ throw new Error(i);
+ result = fooNI3.call(true, 7);
+ if (result.thisValue !== true)
+ throw new Error(i);
+}
+
+
+putFuncToPrivateName(function () { "use strict"; return this; });
+let bodyText = `
+for (let i = 0; i < 100; i++) {
+ if (a + i === 100)
+ return a;
+}
+`;
+var foo4 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} ");
+var fooNI4 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} ");
+noInline(fooNI4);
+function baz4() {
+ return foo4.call(true, 0);
+}
+noInline(baz4);
+
+for (let i = 0; i < 100000; i++) {
+ let result = foo4.call(true, 0);
+ if (result.thisValue !== true || result.otherValue !== "hello")
+ throw new Error(i);
+ result = baz4.call();
+ if (result.thisValue !== true || result.otherValue !== "hello")
+ throw new Error(i);
+ result = fooNI4.call(true, 0);
+ if (result.thisValue !== true || result.otherValue !== "hello")
+ throw new Error(i);
+ result = fooNI4.call(true, 1);
+ if (result !== 1)
+ throw new Error(i);
+ result = fooNI4.call(true, "");
+ if (result.thisValue !== true || result.otherValue !== "hello")
+ throw new Error(i);
+}
+
+var testFunc = function () { "use strict"; return this; }
+noInline(testFunc);
+putFuncToPrivateName(testFunc);
+
+var foo5 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} ");
+var fooNI5 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} ");
+noInline(fooNI5);
+function baz5() {
+ return foo5.call(true, 0);
+}
+noInline(baz5);
+
+for (let i = 0; i < 100000; i++) {
+ let result = foo5.call(true, 0);
+ if (result.thisValue !== true || result.otherValue !== "hello")
+ throw new Error(i);
+ result = baz5.call();
+ if (result.thisValue !== true || result.otherValue !== "hello")
+ throw new Error(i);
+ result = fooNI5.call(true, 0);
+ if (result.thisValue !== true || result.otherValue !== "hello")
+ throw new Error(i);
+ result = fooNI5.call(true, 1);
+ if (result !== 1)
+ throw new Error(i);
+ result = fooNI5.call(true, "");
+ if (result.thisValue !== true || result.otherValue !== "hello")
+ throw new Error(i);
+}
+
+putFuncToPrivateName(function() { return arguments; });
+var foo6 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} ");
+function baz6() {
+ "use strict"
+ return foo6.apply(this, arguments);
+}
+noInline(baz6);
+
+function arrayEq(a, b) {
+ if (a.length !== b.length)
+ throw new Error();
+ for (let i = 0; i < a.length; i++) {
+ if (a[i] !== b[i])
+ throw new Error();
+ }
+}
+let args = ["a", {}, [], Symbol(), 1, 1.234, undefined, null];
+for (let i = 0; i < 100000; i++) {
+ let result = baz6.apply(undefined, args);
+ arrayEq(result, args);
+}