Log Message
DFG should allow inlining of op_call_varargs calls https://bugs.webkit.org/show_bug.cgi?id=127538
Reviewed by Michael Saboff and Mark Hahnenberg. Previously, op_call_varargs was directly lowered to a Call by the ByteCodeParser and we bypassed all of the call optimizations (intrinsics, inlining) provided by ByteCodeParser::handleCall(). This changes op_call_varargs handling so that it first sets up the stack to look like it would have if it had been a normal call, and then it calls handleCall(). This gives us inlining of op_call_varargs. * bytecode/CallLinkStatus.cpp: (JSC::CallLinkStatus::computeFromLLInt): * bytecode/CodeOrigin.h: * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::addCall): (JSC::DFG::ByteCodeParser::handleCall): (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGGraph.cpp: (JSC::DFG::Graph::isLiveInBytecode): * tests/stress/inline-call-varargs-and-call.js: Added. (foo): (bar): (baz): * tests/stress/inline-call-varargs.js: Added. (foo): (bar): (baz):
Modified Paths
- branches/jsCStack/Source/_javascript_Core/ChangeLog
- branches/jsCStack/Source/_javascript_Core/bytecode/CallLinkStatus.cpp
- branches/jsCStack/Source/_javascript_Core/bytecode/CodeOrigin.h
- branches/jsCStack/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp
- branches/jsCStack/Source/_javascript_Core/dfg/DFGGraph.cpp
Added Paths
Diff
Modified: branches/jsCStack/Source/_javascript_Core/ChangeLog (162738 => 162739)
--- branches/jsCStack/Source/_javascript_Core/ChangeLog 2014-01-25 00:52:21 UTC (rev 162738)
+++ branches/jsCStack/Source/_javascript_Core/ChangeLog 2014-01-25 00:59:29 UTC (rev 162739)
@@ -1,3 +1,36 @@
+2014-01-24 Filip Pizlo <fpi...@apple.com>
+
+ DFG should allow inlining of op_call_varargs calls
+ https://bugs.webkit.org/show_bug.cgi?id=127538
+
+ Reviewed by Michael Saboff and Mark Hahnenberg.
+
+ Previously, op_call_varargs was directly lowered to a Call by the ByteCodeParser and
+ we bypassed all of the call optimizations (intrinsics, inlining) provided by
+ ByteCodeParser::handleCall().
+
+ This changes op_call_varargs handling so that it first sets up the stack to look like
+ it would have if it had been a normal call, and then it calls handleCall(). This
+ gives us inlining of op_call_varargs.
+
+ * bytecode/CallLinkStatus.cpp:
+ (JSC::CallLinkStatus::computeFromLLInt):
+ * bytecode/CodeOrigin.h:
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::addCall):
+ (JSC::DFG::ByteCodeParser::handleCall):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::isLiveInBytecode):
+ * tests/stress/inline-call-varargs-and-call.js: Added.
+ (foo):
+ (bar):
+ (baz):
+ * tests/stress/inline-call-varargs.js: Added.
+ (foo):
+ (bar):
+ (baz):
+
2014-01-24 Mark Hahnenberg <mhahnenb...@apple.com>
Merge branch up to ToT r162711.
Modified: branches/jsCStack/Source/_javascript_Core/bytecode/CallLinkStatus.cpp (162738 => 162739)
--- branches/jsCStack/Source/_javascript_Core/bytecode/CallLinkStatus.cpp 2014-01-25 00:52:21 UTC (rev 162738)
+++ branches/jsCStack/Source/_javascript_Core/bytecode/CallLinkStatus.cpp 2014-01-25 00:59:29 UTC (rev 162739)
@@ -92,7 +92,13 @@
return takesSlowPath();
}
+ VM& vm = *profiledBlock->vm();
+
Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex;
+ OpcodeID op = vm.interpreter->getOpcodeID(instruction[0].u.opcode);
+ if (op != op_call && op != op_construct)
+ return CallLinkStatus();
+
LLIntCallLinkInfo* callLinkInfo = instruction[5].u.callLinkInfo;
return CallLinkStatus(callLinkInfo->lastSeenCallee.get());
Modified: branches/jsCStack/Source/_javascript_Core/bytecode/CodeOrigin.h (162738 => 162739)
--- branches/jsCStack/Source/_javascript_Core/bytecode/CodeOrigin.h 2014-01-25 00:52:21 UTC (rev 162738)
+++ branches/jsCStack/Source/_javascript_Core/bytecode/CodeOrigin.h 2014-01-25 00:59:29 UTC (rev 162739)
@@ -105,7 +105,7 @@
};
struct InlineCallFrame {
- Vector<ValueRecovery> arguments;
+ Vector<ValueRecovery> arguments; // Includes 'this'.
WriteBarrier<ScriptExecutable> executable;
ValueRecovery calleeRecovery;
CodeOrigin caller;
Modified: branches/jsCStack/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (162738 => 162739)
--- branches/jsCStack/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2014-01-25 00:52:21 UTC (rev 162738)
+++ branches/jsCStack/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2014-01-25 00:59:29 UTC (rev 162739)
@@ -40,6 +40,7 @@
#include "Operations.h"
#include "PreciseJumpTargets.h"
#include "PutByIdStatus.h"
+#include "StackAlignment.h"
#include "StringConstructor.h"
#include <wtf/CommaPrinter.h>
#include <wtf/HashMap.h>
@@ -160,7 +161,8 @@
bool handleMinMax(int resultOperand, NodeType op, int registerOffset, int argumentCountIncludingThis);
// Handle calls. This resolves issues surrounding inlining and intrinsics.
- void handleCall(Instruction* currentInstruction, NodeType op, CodeSpecializationKind);
+ void handleCall(int result, NodeType op, CodeSpecializationKind, unsigned instructionSize, int callee, int argCount, int registerOffset);
+ void handleCall(Instruction* pc, NodeType op, CodeSpecializationKind kind);
void emitFunctionChecks(const CallLinkStatus&, Node* callTarget, int registerOffset, CodeSpecializationKind);
void emitArgumentPhantoms(int registerOffset, int argumentCountIncludingThis, CodeSpecializationKind);
// Handle inlining. Return true if it succeeded, false if we need to plant a call.
@@ -773,23 +775,21 @@
m_numPassedVarArgs++;
}
- Node* addCall(Instruction* currentInstruction, NodeType op)
+ Node* addCall(int result, NodeType op, int callee, int argCount, int registerOffset)
{
SpeculatedType prediction = getPrediction();
- addVarArgChild(get(VirtualRegister(currentInstruction[2].u.operand)));
- int argCount = currentInstruction[3].u.operand;
+ addVarArgChild(get(VirtualRegister(callee)));
size_t parameterSlots = JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize + argCount;
if (parameterSlots > m_parameterSlots)
m_parameterSlots = parameterSlots;
- int registerOffset = -currentInstruction[4].u.operand;
int dummyThisArgument = op == Call ? 0 : 1;
for (int i = 0 + dummyThisArgument; i < argCount; ++i)
addVarArgChild(get(virtualRegisterForArgument(i, registerOffset)));
Node* call = addToGraph(Node::VarArg, op, OpInfo(0), OpInfo(prediction));
- set(VirtualRegister(currentInstruction[1].u.operand), call);
+ set(VirtualRegister(result), call);
return call;
}
@@ -1147,12 +1147,21 @@
m_currentIndex += OPCODE_LENGTH(name); \
return shouldContinueParsing
+void ByteCodeParser::handleCall(Instruction* pc, NodeType op, CodeSpecializationKind kind)
+{
+ ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct));
+ handleCall(
+ pc[1].u.operand, op, kind, OPCODE_LENGTH(op_call),
+ pc[2].u.operand, pc[3].u.operand, -pc[4].u.operand);
+}
-void ByteCodeParser::handleCall(Instruction* currentInstruction, NodeType op, CodeSpecializationKind kind)
+void ByteCodeParser::handleCall(
+ int result, NodeType op, CodeSpecializationKind kind, unsigned instructionSize,
+ int callee, int argumentCountIncludingThis, int registerOffset)
{
- ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct));
+ ASSERT(registerOffset <= 0);
- Node* callTarget = get(VirtualRegister(currentInstruction[2].u.operand));
+ Node* callTarget = get(VirtualRegister(callee));
CallLinkStatus callLinkStatus;
@@ -1165,19 +1174,15 @@
// Oddly, this conflates calls that haven't executed with calls that behaved sufficiently polymorphically
// that we cannot optimize them.
- addCall(currentInstruction, op);
+ addCall(result, op, callee, argumentCountIncludingThis, registerOffset);
return;
}
- int argumentCountIncludingThis = currentInstruction[3].u.operand;
- int registerOffset = -currentInstruction[4].u.operand;
-
- int resultOperand = currentInstruction[1].u.operand;
- unsigned nextOffset = m_currentIndex + OPCODE_LENGTH(op_call);
+ unsigned nextOffset = m_currentIndex + instructionSize;
SpeculatedType prediction = getPrediction();
if (InternalFunction* function = callLinkStatus.internalFunction()) {
- if (handleConstantInternalFunction(resultOperand, function, registerOffset, argumentCountIncludingThis, prediction, kind)) {
+ if (handleConstantInternalFunction(result, function, registerOffset, argumentCountIncludingThis, prediction, kind)) {
// This phantoming has to be *after* the code for the intrinsic, to signify that
// the inputs must be kept alive whatever exits the intrinsic may do.
addToGraph(Phantom, callTarget);
@@ -1186,7 +1191,7 @@
}
// Can only handle this using the generic call handler.
- addCall(currentInstruction, op);
+ addCall(result, op, callee, argumentCountIncludingThis, registerOffset);
return;
}
@@ -1194,7 +1199,7 @@
if (intrinsic != NoIntrinsic) {
emitFunctionChecks(callLinkStatus, callTarget, registerOffset, kind);
- if (handleIntrinsic(resultOperand, intrinsic, registerOffset, argumentCountIncludingThis, prediction)) {
+ if (handleIntrinsic(result, intrinsic, registerOffset, argumentCountIncludingThis, prediction)) {
// This phantoming has to be *after* the code for the intrinsic, to signify that
// the inputs must be kept alive whatever exits the intrinsic may do.
addToGraph(Phantom, callTarget);
@@ -1203,13 +1208,13 @@
m_graph.compilation()->noticeInlinedCall();
return;
}
- } else if (handleInlining(callTarget, resultOperand, callLinkStatus, registerOffset, argumentCountIncludingThis, nextOffset, kind)) {
+ } else if (handleInlining(callTarget, result, callLinkStatus, registerOffset, argumentCountIncludingThis, nextOffset, kind)) {
if (m_graph.compilation())
m_graph.compilation()->noticeInlinedCall();
return;
}
- addCall(currentInstruction, op);
+ addCall(result, op, callee, argumentCountIncludingThis, registerOffset);
}
void ByteCodeParser::emitFunctionChecks(const CallLinkStatus& callLinkStatus, Node* callTarget, int registerOffset, CodeSpecializationKind kind)
@@ -2963,31 +2968,41 @@
NEXT_OPCODE(op_construct);
case op_call_varargs: {
+ int result = currentInstruction[1].u.operand;
+ int callee = currentInstruction[2].u.operand;
+ int thisReg = currentInstruction[3].u.operand;
+ int arguments = currentInstruction[4].u.operand;
+ int firstFreeReg = currentInstruction[5].u.operand;
+
ASSERT(inlineCallFrame());
- ASSERT(currentInstruction[4].u.operand == m_inlineStackTop->m_codeBlock->argumentsRegister().offset());
+ ASSERT_UNUSED(arguments, arguments == m_inlineStackTop->m_codeBlock->argumentsRegister().offset());
ASSERT(!m_inlineStackTop->m_codeBlock->symbolTable()->slowArguments());
- // It would be cool to funnel this into handleCall() so that it can handle
- // inlining. But currently that won't be profitable anyway, since none of the
- // uses of call_varargs will be inlineable. So we set this up manually and
- // without inline/intrinsic detection.
-
- SpeculatedType prediction = getPrediction();
-
+
addToGraph(CheckArgumentsNotCreated);
unsigned argCount = inlineCallFrame()->arguments.size();
- size_t parameterSlots = JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize + argCount;
- if (parameterSlots > m_parameterSlots)
- m_parameterSlots = parameterSlots;
- addVarArgChild(get(VirtualRegister(currentInstruction[2].u.operand))); // callee
- addVarArgChild(get(VirtualRegister(currentInstruction[3].u.operand))); // this
+ // Let's compute the register offset. We start with the last used register, and
+ // then adjust for the things we want in the call frame.
+ int registerOffset = firstFreeReg + 1;
+ registerOffset -= argCount; // We will be passing some arguments.
+ registerOffset -= JSStack::CallFrameHeaderSize; // We will pretend to have a call frame header.
+
+ // Get the alignment right.
+ registerOffset = -WTF::roundUpToMultipleOf(
+ stackAlignmentRegisters(),
+ -registerOffset);
+
+ // The bytecode wouldn't have set up the arguments. But we'll do it and make it
+ // look like the bytecode had done it.
+ int nextRegister = registerOffset + JSStack::CallFrameHeaderSize;
+ set(VirtualRegister(nextRegister++), get(VirtualRegister(thisReg)), ImmediateSet);
for (unsigned argument = 1; argument < argCount; ++argument)
- addVarArgChild(get(virtualRegisterForArgument(argument)));
+ set(VirtualRegister(nextRegister++), get(virtualRegisterForArgument(argument)), ImmediateSet);
- set(VirtualRegister(currentInstruction[1].u.operand),
- addToGraph(Node::VarArg, Call, OpInfo(0), OpInfo(prediction)));
-
+ handleCall(
+ result, Call, CodeForCall, OPCODE_LENGTH(op_call_varargs),
+ callee, argCount, registerOffset);
NEXT_OPCODE(op_call_varargs);
}
Modified: branches/jsCStack/Source/_javascript_Core/dfg/DFGGraph.cpp (162738 => 162739)
--- branches/jsCStack/Source/_javascript_Core/dfg/DFGGraph.cpp 2014-01-25 00:52:21 UTC (rev 162738)
+++ branches/jsCStack/Source/_javascript_Core/dfg/DFGGraph.cpp 2014-01-25 00:59:29 UTC (rev 162739)
@@ -680,10 +680,10 @@
bool Graph::isLiveInBytecode(VirtualRegister operand, CodeOrigin codeOrigin)
{
for (;;) {
+ VirtualRegister reg = VirtualRegister(
+ operand.offset() - codeOrigin.stackOffset());
+
if (operand.offset() < codeOrigin.stackOffset() + JSStack::CallFrameHeaderSize) {
- VirtualRegister reg = VirtualRegister(
- operand.offset() - codeOrigin.stackOffset());
-
if (reg.isArgument()) {
RELEASE_ASSERT(reg.offset() < JSStack::CallFrameHeaderSize);
@@ -702,10 +702,18 @@
reg.offset(), codeOrigin.bytecodeIndex);
}
- if (!codeOrigin.inlineCallFrame)
+ InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame;
+ if (!inlineCallFrame)
break;
+
+ // Arguments are always live. This would be redundant if it wasn't for our
+ // op_call_varargs inlining.
+ if (reg.isArgument()
+ && reg.toArgument()
+ && static_cast<size_t>(reg.toArgument()) < inlineCallFrame->arguments.size())
+ return true;
- codeOrigin = codeOrigin.inlineCallFrame->caller;
+ codeOrigin = inlineCallFrame->caller;
}
return true;
Added: branches/jsCStack/Source/_javascript_Core/tests/stress/inline-call-varargs-and-call.js (0 => 162739)
--- branches/jsCStack/Source/_javascript_Core/tests/stress/inline-call-varargs-and-call.js (rev 0)
+++ branches/jsCStack/Source/_javascript_Core/tests/stress/inline-call-varargs-and-call.js 2014-01-25 00:59:29 UTC (rev 162739)
@@ -0,0 +1,25 @@
+function foo(a, b) {
+ return bar(a, b);
+}
+
+function bar(a, b) {
+ var x = baz.apply(null, arguments);
+ var y = baz(a, b)
+ return x + y * 3;
+}
+
+function baz(a, b) {
+ return a + b * 2;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100000; ++i) {
+ var result = foo(7, 11);
+ if (result != 116)
+ throw "Error: bad result: " + result;
+}
+
+var result = foo(5, 2147483646);
+if (result != 17179869188)
+ throw "Error: bad result: " + result;
Added: branches/jsCStack/Source/_javascript_Core/tests/stress/inline-call-varargs.js (0 => 162739)
--- branches/jsCStack/Source/_javascript_Core/tests/stress/inline-call-varargs.js (rev 0)
+++ branches/jsCStack/Source/_javascript_Core/tests/stress/inline-call-varargs.js 2014-01-25 00:59:29 UTC (rev 162739)
@@ -0,0 +1,23 @@
+function foo(a, b) {
+ return bar(a, b);
+}
+
+function bar(a, b) {
+ return baz.apply(null, arguments);
+}
+
+function baz(a, b) {
+ return a + b * 2;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100000; ++i) {
+ var result = foo(7, 11);
+ if (result != 29)
+ throw "Error: bad result: " + result;
+}
+
+var result = foo(5, 2147483646);
+if (result != 4294967297)
+ throw "Error: bad result: " + result;
_______________________________________________ webkit-changes mailing list webkit-changes@lists.webkit.org https://lists.webkit.org/mailman/listinfo/webkit-changes