Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (202002 => 202003)
--- trunk/Source/_javascript_Core/ChangeLog 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-06-13 21:05:36 UTC (rev 202003)
@@ -1,3 +1,123 @@
+2016-06-12 Keith Miller <[email protected]>
+
+ 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 <[email protected]>
Unreviewed, follow up patch for r201964
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.h (202002 => 202003)
--- trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -43,6 +43,7 @@
macro(argumentCount) \
macro(assert) \
macro(isObject) \
+ macro(tailCallForwardArguments) \
macro(tryGetById) \
macro(putByValDirect) \
macro(toString)
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeList.json (202002 => 202003)
--- trunk/Source/_javascript_Core/bytecode/BytecodeList.json 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeList.json 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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 },
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h (202002 => 202003)
--- trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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:
Modified: trunk/Source/_javascript_Core/bytecode/CallLinkInfo.h (202002 => 202003)
--- trunk/Source/_javascript_Core/bytecode/CallLinkInfo.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/bytecode/CallLinkInfo.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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;
}
Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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;
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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);
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (202002 => 202003)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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,
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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)
Modified: trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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()) {
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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,9 +1771,12 @@
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
// callTargetNode because the other 2 are still in use and alive at this point.
@@ -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);
}
Modified: trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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:
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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()) {
Modified: trunk/Source/_javascript_Core/dfg/DFGPreciseLocalClobberize.h (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGPreciseLocalClobberize.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGPreciseLocalClobberize.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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;)
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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;
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -5952,8 +5952,12 @@
void SpeculativeJIT::compileForwardVarargs(Node* node)
{
LoadVarargsData* data = ""
- 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();
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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());
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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();
Modified: trunk/Source/_javascript_Core/dfg/DFGVarargsForwardingPhase.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/dfg/DFGVarargsForwardingPhase.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/dfg/DFGVarargsForwardingPhase.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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;
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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 = ""
- 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));
Modified: trunk/Source/_javascript_Core/interpreter/Interpreter.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/interpreter/Interpreter.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/interpreter/Interpreter.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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)
Modified: trunk/Source/_javascript_Core/interpreter/Interpreter.h (202002 => 202003)
--- trunk/Source/_javascript_Core/interpreter/Interpreter.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/interpreter/Interpreter.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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
Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/jit/JIT.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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)
Modified: trunk/Source/_javascript_Core/jit/JIT.h (202002 => 202003)
--- trunk/Source/_javascript_Core/jit/JIT.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/jit/JIT.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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&);
Modified: trunk/Source/_javascript_Core/jit/JITCall.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/jit/JITCall.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/jit/JITCall.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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.
-
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)
{
Modified: trunk/Source/_javascript_Core/jit/JITCall32_64.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/jit/JITCall32_64.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/jit/JITCall32_64.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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;
Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/jit/JITOperations.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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();
Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (202002 => 202003)
--- trunk/Source/_javascript_Core/jit/JITOperations.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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;
Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp (202002 => 202003)
--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.cpp 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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_NO_SET_PC();
+ LLINT_BEGIN();
// 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;
+ // - Set up a call frame with the same arguments as the current frame.
- setupVarargsFrameAndSetThis(exec, execCallee, LLINT_OP_C(3).jsValue(), LLINT_OP_C(4).jsValue(), pc[6].u.operand, vm.varargsLength);
+ unsigned numUsedStackSlots = -pc[5].u.operand;
+
+ unsigned arguments = sizeFrameForForwardArguments(exec, &vm.interpreter->stack(), numUsedStackSlots);
LLINT_CALL_CHECK_EXCEPTION(exec, exec);
-
- execCallee->uncheckedR(JSStack::Callee) = calleeAsValue;
- execCallee->setCallerFrame(exec);
- exec->setCurrentVPC(pc);
-
- return setUpCall(execCallee, pc, CodeForCall, calleeAsValue);
+
+ ExecState* execCallee = calleeFrameForVarargs(exec, numUsedStackSlots, arguments + 1);
+
+ vm.varargsLength = arguments;
+ vm.newCallFrameReturnValue = execCallee;
+
+ LLINT_RETURN_CALLEE_FRAME(execCallee);
}
-
-LLINT_SLOW_PATH_DECL(slow_path_construct_varargs)
+
+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);
-
+
+ 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;
- execCallee->setCallerFrame(exec);
exec->setCurrentVPC(pc);
-
- return setUpCall(execCallee, pc, CodeForConstruct, 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)
+{
+ return varargsSetup(exec, pc, CodeForConstruct, SetArgumentsWith::Object);
+}
+
LLINT_SLOW_PATH_DECL(slow_path_call_eval)
{
Modified: trunk/Source/_javascript_Core/llint/LLIntSlowPaths.h (202002 => 202003)
--- trunk/Source/_javascript_Core/llint/LLIntSlowPaths.h 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/llint/LLIntSlowPaths.h 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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);
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm (202002 => 202003)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2016-06-13 20:06:22 UTC (rev 202002)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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:
Added: trunk/Source/_javascript_Core/tests/stress/tailCallForwardArguments.js (0 => 202003)
--- trunk/Source/_javascript_Core/tests/stress/tailCallForwardArguments.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/tailCallForwardArguments.js 2016-06-13 21:05:36 UTC (rev 202003)
@@ -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);
+}