Commit 4500e353 authored by fpizlo@apple.com's avatar fpizlo@apple.com

Array and object allocations via 'new Object' or 'new Array' should be inlined...

Array and object allocations via 'new Object' or 'new Array' should be inlined in bytecode to allow allocation site profiling
https://bugs.webkit.org/show_bug.cgi?id=99557

Reviewed by Geoffrey Garen.

This uses the old jneq_ptr trick to allow for the bytecode to "see" that the
operation in question is what we almost certainly know it to be.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* bytecode/Opcode.h:
(JSC):
(JSC::padOpcodeName):
* bytecode/SpecialPointer.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitCall):
(JSC::BytecodeGenerator::emitCallEval):
(JSC::BytecodeGenerator::expectedFunctionForIdentifier):
(JSC):
(JSC::BytecodeGenerator::emitExpectedFunctionSnippet):
(JSC::BytecodeGenerator::emitConstruct):
* bytecompiler/BytecodeGenerator.h:
(BytecodeGenerator):
* bytecompiler/NodesCodegen.cpp:
(JSC::NewExprNode::emitBytecode):
(JSC::FunctionCallValueNode::emitBytecode):
(JSC::FunctionCallResolveNode::emitBytecode):
(JSC::FunctionCallBracketNode::emitBytecode):
(JSC::FunctionCallDotNode::emitBytecode):
(JSC::CallFunctionCallDotNode::emitBytecode):
(JSC::ApplyFunctionCallDotNode::emitBytecode):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.h:
(JSC::DFG::canCompileOpcode):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
(JIT):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_new_array_with_size):
(JSC):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
(JSC):
* jit/JITStubs.h:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
(LLInt):
* llint/LLIntSlowPaths.h:
(LLInt):
* llint/LowLevelInterpreter.asm:
* runtime/ArrayConstructor.cpp:
(JSC::constructArrayWithSizeQuirk):
(JSC):
* runtime/ArrayConstructor.h:
(JSC):
* runtime/CommonIdentifiers.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::reset):
(JSC):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@131644 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent decef1e6
2012-10-17 Filip Pizlo <fpizlo@apple.com>
Array and object allocations via 'new Object' or 'new Array' should be inlined in bytecode to allow allocation site profiling
https://bugs.webkit.org/show_bug.cgi?id=99557
Reviewed by Geoffrey Garen.
This uses the old jneq_ptr trick to allow for the bytecode to "see" that the
operation in question is what we almost certainly know it to be.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* bytecode/Opcode.h:
(JSC):
(JSC::padOpcodeName):
* bytecode/SpecialPointer.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitCall):
(JSC::BytecodeGenerator::emitCallEval):
(JSC::BytecodeGenerator::expectedFunctionForIdentifier):
(JSC):
(JSC::BytecodeGenerator::emitExpectedFunctionSnippet):
(JSC::BytecodeGenerator::emitConstruct):
* bytecompiler/BytecodeGenerator.h:
(BytecodeGenerator):
* bytecompiler/NodesCodegen.cpp:
(JSC::NewExprNode::emitBytecode):
(JSC::FunctionCallValueNode::emitBytecode):
(JSC::FunctionCallResolveNode::emitBytecode):
(JSC::FunctionCallBracketNode::emitBytecode):
(JSC::FunctionCallDotNode::emitBytecode):
(JSC::CallFunctionCallDotNode::emitBytecode):
(JSC::ApplyFunctionCallDotNode::emitBytecode):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.h:
(JSC::DFG::canCompileOpcode):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
(JIT):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_new_array_with_size):
(JSC):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
(JSC):
* jit/JITStubs.h:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
(LLInt):
* llint/LLIntSlowPaths.h:
(LLInt):
* llint/LowLevelInterpreter.asm:
* runtime/ArrayConstructor.cpp:
(JSC::constructArrayWithSizeQuirk):
(JSC):
* runtime/ArrayConstructor.h:
(JSC):
* runtime/CommonIdentifiers.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::reset):
(JSC):
2012-10-17 Filip Pizlo <fpizlo@apple.com>
JIT op_get_by_pname should call cti_get_by_val_generic and not cti_get_by_val
......
......@@ -704,6 +704,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
dumpBytecodeCommentAndNewLine(location);
break;
}
case op_new_array_with_size: {
int dst = (++it)->u.operand;
int length = (++it)->u.operand;
dataLog("[%4d] new_array_with_size\t %s, %s", location, registerName(exec, dst).data(), registerName(exec, length).data());
dumpBytecodeCommentAndNewLine(location);
break;
}
case op_new_array_buffer: {
int dst = (++it)->u.operand;
int argv = (++it)->u.operand;
......@@ -1245,9 +1252,9 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
}
case op_jneq_ptr: {
int r0 = (++it)->u.operand;
void* pointer = (++it)->u.pointer;
Special::Pointer pointer = (++it)->u.specialPointer;
int offset = (++it)->u.operand;
dataLog("[%4d] jneq_ptr\t\t %s, %p, %d(->%d)", location, registerName(exec, r0).data(), pointer, offset, location + offset);
dataLog("[%4d] jneq_ptr\t\t %s, %d (%p), %d(->%d)", location, registerName(exec, r0).data(), pointer, m_globalObject->actualPointerFor(pointer), offset, location + offset);
dumpBytecodeCommentAndNewLine(location);
break;
}
......
......@@ -49,6 +49,7 @@ namespace JSC {
\
macro(op_new_object, 2) \
macro(op_new_array, 4) \
macro(op_new_array_with_size, 3) \
macro(op_new_array_buffer, 4) \
macro(op_new_regexp, 3) \
macro(op_mov, 3) \
......
......@@ -35,6 +35,8 @@ namespace Special {
enum Pointer {
CallFunction,
ApplyFunction,
ObjectConstructor,
ArrayConstructor,
TableSize // Not a real special pointer. Use this to determine the number of pointers.
};
} // namespace Special
......
......@@ -2013,9 +2013,9 @@ RegisterID* BytecodeGenerator::emitNewFunctionExpression(RegisterID* r0, FuncExp
return r0;
}
RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset)
RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset)
{
return emitCall(op_call, dst, func, callArguments, divot, startOffset, endOffset);
return emitCall(op_call, dst, func, expectedFunction, callArguments, divot, startOffset, endOffset);
}
void BytecodeGenerator::createArgumentsIfNecessary()
......@@ -2048,10 +2048,85 @@ void BytecodeGenerator::createActivationIfNecessary()
RegisterID* BytecodeGenerator::emitCallEval(RegisterID* dst, RegisterID* func, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset)
{
return emitCall(op_call_eval, dst, func, callArguments, divot, startOffset, endOffset);
return emitCall(op_call_eval, dst, func, NoExpectedFunction, callArguments, divot, startOffset, endOffset);
}
RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, RegisterID* func, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset)
ExpectedFunction BytecodeGenerator::expectedFunctionForIdentifier(const Identifier& identifier)
{
if (identifier == m_globalData->propertyNames->Object)
return ExpectObjectConstructor;
if (identifier == m_globalData->propertyNames->Array)
return ExpectArrayConstructor;
return NoExpectedFunction;
}
ExpectedFunction BytecodeGenerator::emitExpectedFunctionSnippet(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, Label* done)
{
RefPtr<Label> realCall = newLabel();
switch (expectedFunction) {
case ExpectObjectConstructor: {
// If the number of arguments is non-zero, then we can't do anything interesting.
if (callArguments.argumentCountIncludingThis() >= 2)
return NoExpectedFunction;
size_t begin = instructions().size();
emitOpcode(op_jneq_ptr);
instructions().append(func->index());
instructions().append(Special::ObjectConstructor);
instructions().append(realCall->bind(begin, instructions().size()));
if (dst != ignoredResult()) {
emitOpcode(op_new_object);
instructions().append(dst->index());
}
break;
}
case ExpectArrayConstructor: {
// If you're doing anything other than "new Array()" or "new Array(foo)" then we
// don't do inline it, for now. The only reason is that call arguments are in
// the opposite order of what op_new_array expects, so we'd either need to change
// how op_new_array works or we'd need an op_new_array_reverse. Neither of these
// things sounds like it's worth it.
if (callArguments.argumentCountIncludingThis() > 2)
return NoExpectedFunction;
size_t begin = instructions().size();
emitOpcode(op_jneq_ptr);
instructions().append(func->index());
instructions().append(Special::ArrayConstructor);
instructions().append(realCall->bind(begin, instructions().size()));
if (dst != ignoredResult()) {
if (callArguments.argumentCountIncludingThis() == 2) {
emitOpcode(op_new_array_with_size);
instructions().append(dst->index());
instructions().append(callArguments.argumentRegister(0)->index());
} else {
ASSERT(callArguments.argumentCountIncludingThis() == 1);
emitOpcode(op_new_array);
instructions().append(dst->index());
instructions().append(0);
instructions().append(0);
}
}
break;
}
default:
ASSERT(expectedFunction == NoExpectedFunction);
return NoExpectedFunction;
}
size_t begin = instructions().size();
emitOpcode(op_jmp);
instructions().append(done->bind(begin, instructions().size()));
emitLabel(realCall.get());
return expectedFunction;
}
RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset)
{
ASSERT(opcodeID == op_call || opcodeID == op_call_eval);
ASSERT(func->refCount());
......@@ -2076,6 +2151,9 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi
emitExpressionInfo(divot, startOffset, endOffset);
RefPtr<Label> done = newLabel();
expectedFunction = emitExpectedFunctionSnippet(dst, func, expectedFunction, callArguments, done.get());
// Emit call.
ArrayProfile* arrayProfile = newArrayProfile();
emitOpcode(opcodeID);
......@@ -2093,6 +2171,9 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi
instructions().append(dst->index()); // dst
instructions().append(profile);
}
if (expectedFunction != NoExpectedFunction)
emitLabel(done.get());
if (m_shouldEmitProfileHooks) {
emitOpcode(op_profile_did_call);
......@@ -2162,7 +2243,7 @@ RegisterID* BytecodeGenerator::emitUnaryNoDstOp(OpcodeID opcodeID, RegisterID* s
return src;
}
RegisterID* BytecodeGenerator::emitConstruct(RegisterID* dst, RegisterID* func, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset)
RegisterID* BytecodeGenerator::emitConstruct(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset)
{
ASSERT(func->refCount());
......@@ -2187,6 +2268,9 @@ RegisterID* BytecodeGenerator::emitConstruct(RegisterID* dst, RegisterID* func,
callFrame.append(newTemporary());
emitExpressionInfo(divot, startOffset, endOffset);
RefPtr<Label> done = newLabel();
expectedFunction = emitExpectedFunctionSnippet(dst, func, expectedFunction, callArguments, done.get());
emitOpcode(op_construct);
instructions().append(func->index()); // func
......@@ -2204,6 +2288,9 @@ RegisterID* BytecodeGenerator::emitConstruct(RegisterID* dst, RegisterID* func,
instructions().append(profile);
}
if (expectedFunction != NoExpectedFunction)
emitLabel(done.get());
if (m_shouldEmitProfileHooks) {
emitOpcode(op_profile_did_call);
instructions().append(callArguments.profileHookRegister()->index());
......
......@@ -51,6 +51,12 @@ namespace JSC {
class Label;
class JSScope;
enum ExpectedFunction {
NoExpectedFunction,
ExpectObjectConstructor,
ExpectArrayConstructor
};
class CallArguments {
public:
CallArguments(BytecodeGenerator& generator, ArgumentsNode* argumentsNode);
......@@ -483,8 +489,9 @@ namespace JSC {
RegisterID* emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property);
RegisterID* emitPutByIndex(RegisterID* base, unsigned index, RegisterID* value);
void emitPutGetterSetter(RegisterID* base, const Identifier& property, RegisterID* getter, RegisterID* setter);
RegisterID* emitCall(RegisterID* dst, RegisterID* func, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset);
ExpectedFunction expectedFunctionForIdentifier(const Identifier&);
RegisterID* emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset);
RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset);
RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, RegisterID* profileHookRegister, unsigned divot, unsigned startOffset, unsigned endOffset);
RegisterID* emitLoadVarargs(RegisterID* argCountDst, RegisterID* thisRegister, RegisterID* args);
......@@ -492,7 +499,7 @@ namespace JSC {
RegisterID* emitReturn(RegisterID* src);
RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); }
RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset);
RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset);
RegisterID* emitStrcat(RegisterID* dst, RegisterID* src, int count);
void emitToPrimitive(RegisterID* dst, RegisterID* src);
......@@ -590,7 +597,12 @@ namespace JSC {
typedef HashMap<double, JSValue> NumberMap;
typedef HashMap<StringImpl*, JSString*, IdentifierRepHash> IdentifierStringMap;
RegisterID* emitCall(OpcodeID, RegisterID* dst, RegisterID* func, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset);
// Helper for emitCall() and emitConstruct(). This works because the set of
// expected functions have identical behavior for both call and construct
// (i.e. "Object()" is identical to "new Object()").
ExpectedFunction emitExpectedFunctionSnippet(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, Label* done);
RegisterID* emitCall(OpcodeID, RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset);
RegisterID* newRegister();
......
......@@ -366,9 +366,14 @@ RegisterID* ArgumentListNode::emitBytecode(BytecodeGenerator& generator, Registe
RegisterID* NewExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
ExpectedFunction expectedFunction;
if (m_expr->isResolveNode())
expectedFunction = generator.expectedFunctionForIdentifier(static_cast<ResolveNode*>(m_expr)->identifier());
else
expectedFunction = NoExpectedFunction;
RefPtr<RegisterID> func = generator.emitNode(m_expr);
CallArguments callArguments(generator, m_args);
return generator.emitConstruct(generator.finalDestinationOrIgnored(dst), func.get(), callArguments, divot(), startOffset(), endOffset());
return generator.emitConstruct(generator.finalDestinationOrIgnored(dst), func.get(), expectedFunction, callArguments, divot(), startOffset(), endOffset());
}
inline CallArguments::CallArguments(BytecodeGenerator& generator, ArgumentsNode* argumentsNode)
......@@ -415,20 +420,23 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re
RefPtr<RegisterID> func = generator.emitNode(m_expr);
CallArguments callArguments(generator, m_args);
generator.emitLoad(callArguments.thisRegister(), jsUndefined());
return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset());
return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
}
// ------------------------------ FunctionCallResolveNode ----------------------------------
RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
ExpectedFunction expectedFunction = generator.expectedFunctionForIdentifier(m_ident);
ResolveResult resolveResult = generator.resolve(m_ident);
if (RegisterID* local = resolveResult.local()) {
RefPtr<RegisterID> func = generator.emitMove(generator.tempDestination(dst), local);
CallArguments callArguments(generator, m_args);
generator.emitLoad(callArguments.thisRegister(), jsUndefined());
return generator.emitCall(generator.finalDestinationOrIgnored(dst, callArguments.thisRegister()), func.get(), callArguments, divot(), startOffset(), endOffset());
// This passes NoExpectedFunction because we expect that if the function is in a
// local variable, then it's not one of our built-in constructors.
return generator.emitCall(generator.finalDestinationOrIgnored(dst, callArguments.thisRegister()), func.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
}
if (resolveResult.isStatic()) {
......@@ -436,7 +444,7 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator,
CallArguments callArguments(generator, m_args);
generator.emitGetStaticVar(func.get(), resolveResult, m_ident);
generator.emitLoad(callArguments.thisRegister(), jsUndefined());
return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset());
return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), expectedFunction, callArguments, divot(), startOffset(), endOffset());
}
RefPtr<RegisterID> func = generator.newTemporary();
......@@ -445,7 +453,8 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator,
generator.emitExpressionInfo(identifierStart + m_ident.length(), m_ident.length(), 0);
generator.emitResolveWithThis(callArguments.thisRegister(), func.get(), resolveResult, m_ident);
return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset());
// FIXME: Reconsider passing expectedFunction instead of NoExpectedFunction.
return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), expectedFunction, callArguments, divot(), startOffset(), endOffset());
}
// ------------------------------ FunctionCallBracketNode ----------------------------------
......@@ -458,7 +467,7 @@ RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator,
RefPtr<RegisterID> function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property);
CallArguments callArguments(generator, m_args);
generator.emitMove(callArguments.thisRegister(), base.get());
return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), callArguments, divot(), startOffset(), endOffset());
return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
}
// ------------------------------ FunctionCallDotNode ----------------------------------
......@@ -471,7 +480,7 @@ RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, Regi
generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset);
generator.emitMethodCheck();
generator.emitGetById(function.get(), callArguments.thisRegister(), m_ident);
return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), callArguments, divot(), startOffset(), endOffset());
return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
}
RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
......@@ -491,7 +500,7 @@ RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator,
RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get());
CallArguments callArguments(generator, m_args);
generator.emitNode(callArguments.thisRegister(), oldList->m_expr);
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset());
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
generator.emitJump(end.get());
m_args->m_listNode = oldList;
......@@ -499,7 +508,7 @@ RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator,
RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get());
CallArguments callArguments(generator, m_args);
generator.emitLoad(callArguments.thisRegister(), jsUndefined());
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset());
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
generator.emitJump(end.get());
}
}
......@@ -507,7 +516,7 @@ RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator,
{
CallArguments callArguments(generator, m_args);
generator.emitMove(callArguments.thisRegister(), base.get());
generator.emitCall(finalDestinationOrIgnored.get(), function.get(), callArguments, divot(), startOffset(), endOffset());
generator.emitCall(finalDestinationOrIgnored.get(), function.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
}
generator.emitLabel(end.get());
return finalDestinationOrIgnored.get();
......@@ -544,20 +553,20 @@ RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator,
RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get());
CallArguments callArguments(generator, m_args);
generator.emitNode(callArguments.thisRegister(), oldList->m_expr);
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset());
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
} else {
m_args->m_listNode = m_args->m_listNode->m_next;
RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get());
CallArguments callArguments(generator, m_args);
generator.emitNode(callArguments.thisRegister(), oldList->m_expr);
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset());
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
}
m_args->m_listNode = oldList;
} else {
RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get());
CallArguments callArguments(generator, m_args);
generator.emitLoad(callArguments.thisRegister(), jsUndefined());
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset());
generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
}
} else {
ASSERT(m_args->m_listNode && m_args->m_listNode->m_next);
......@@ -586,7 +595,7 @@ RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator,
{
CallArguments callArguments(generator, m_args);
generator.emitMove(callArguments.thisRegister(), base.get());
generator.emitCall(finalDestinationOrIgnored.get(), function.get(), callArguments, divot(), startOffset(), endOffset());
generator.emitCall(finalDestinationOrIgnored.get(), function.get(), NoExpectedFunction, callArguments, divot(), startOffset(), endOffset());
}
generator.emitLabel(end.get());
return finalDestinationOrIgnored.get();
......
......@@ -1966,6 +1966,12 @@ bool ByteCodeParser::parseBlock(unsigned limit)
NEXT_OPCODE(op_new_array);
}
case op_new_array_with_size: {
int lengthOperand = currentInstruction[2].u.operand;
set(currentInstruction[1].u.operand, addToGraph(NewArrayWithSize, get(lengthOperand)));
NEXT_OPCODE(op_new_array_with_size);
}
case op_new_array_buffer: {
int startConstant = currentInstruction[2].u.operand;
int numConstants = currentInstruction[3].u.operand;
......
......@@ -162,6 +162,7 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi
case op_resolve_global:
case op_new_object:
case op_new_array:
case op_new_array_with_size:
case op_new_array_buffer:
case op_strcat:
case op_to_primitive:
......
......@@ -312,6 +312,7 @@ void JIT::privateCompileMainPass()
DEFINE_OP(op_neq)
DEFINE_OP(op_neq_null)
DEFINE_OP(op_new_array)
DEFINE_OP(op_new_array_with_size)
DEFINE_OP(op_new_array_buffer)
DEFINE_OP(op_new_func)
DEFINE_OP(op_new_func_exp)
......
......@@ -718,6 +718,7 @@ namespace JSC {
void emit_op_neq(Instruction*);
void emit_op_neq_null(Instruction*);
void emit_op_new_array(Instruction*);
void emit_op_new_array_with_size(Instruction*);
void emit_op_new_array_buffer(Instruction*);
void emit_op_new_func(Instruction*);
void emit_op_new_func_exp(Instruction*);
......
......@@ -1694,6 +1694,17 @@ void JIT::emitSlow_op_new_array(Instruction* currentInstruction, Vector<SlowCase
stubCall.call(currentInstruction[1].u.operand);
}
void JIT::emit_op_new_array_with_size(Instruction* currentInstruction)
{
JITStubCall stubCall(this, cti_op_new_array_with_size);
#if USE(JSVALUE64)
stubCall.addArgument(currentInstruction[2].u.operand, regT2);
#else
stubCall.addArgument(currentInstruction[2].u.operand);
#endif
stubCall.call(currentInstruction[1].u.operand);
}
void JIT::emit_op_new_array_buffer(Instruction* currentInstruction)
{
JITStubCall stubCall(this, cti_op_new_array_buffer);
......
......@@ -35,6 +35,7 @@
#include "CommonSlowPaths.h"
#include "Arguments.h"
#include "ArrayConstructor.h"
#include "CallFrame.h"
#include "CodeBlock.h"
#include "CodeProfiling.h"
......@@ -2360,6 +2361,13 @@ DEFINE_STUB_FUNCTION(JSObject*, op_new_array)
return constructArray(stackFrame.callFrame, reinterpret_cast<JSValue*>(&stackFrame.callFrame->registers()[stackFrame.args[0].int32()]), stackFrame.args[1].int32());
}
DEFINE_STUB_FUNCTION(JSObject*, op_new_array_with_size)
{
STUB_INIT_STACK_FRAME(stackFrame);
return constructArrayWithSizeQuirk(stackFrame.callFrame, stackFrame.callFrame->lexicalGlobalObject(), stackFrame.args[0].jsValue());
}
DEFINE_STUB_FUNCTION(JSObject*, op_new_array_buffer)
{
STUB_INIT_STACK_FRAME(stackFrame);
......
......@@ -413,6 +413,7 @@ extern "C" {
EncodedJSValue JIT_STUB cti_op_urshift(STUB_ARGS_DECLARATION) WTF_INTERNAL;
EncodedJSValue JIT_STUB cti_to_object(STUB_ARGS_DECLARATION) WTF_INTERNAL;
JSObject* JIT_STUB cti_op_new_array(STUB_ARGS_DECLARATION) WTF_INTERNAL;
JSObject* JIT_STUB cti_op_new_array_with_size(STUB_ARGS_DECLARATION) WTF_INTERNAL;
JSObject* JIT_STUB cti_op_new_array_buffer(STUB_ARGS_DECLARATION) WTF_INTERNAL;
JSObject* JIT_STUB cti_op_new_func(STUB_ARGS_DECLARATION) WTF_INTERNAL;
JSObject* JIT_STUB cti_op_new_func_exp(STUB_ARGS_DECLARATION) WTF_INTERNAL;
......
......@@ -29,6 +29,7 @@
#if ENABLE(LLINT)
#include "Arguments.h"
#include "ArrayConstructor.h"
#include "CallFrame.h"
#include "CommonSlowPaths.h"
#include "GetterSetter.h"
......@@ -504,6 +505,12 @@ LLINT_SLOW_PATH_DECL(slow_path_new_array)
LLINT_RETURN(constructArray(exec, bitwise_cast<JSValue*>(&LLINT_OP(2)), pc[3].u.operand));
}
LLINT_SLOW_PATH_DECL(slow_path_new_array_with_size)
{
LLINT_BEGIN();
LLINT_RETURN(constructArrayWithSizeQuirk(exec, exec->lexicalGlobalObject(), LLINT_OP_C(2).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_new_array_buffer)
{
LLINT_BEGIN();
......
......@@ -121,6 +121,7 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_create_this);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_convert_this);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_object);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_array);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_array_with_size);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_array_buffer);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_regexp);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_not);
......
......@@ -428,6 +428,12 @@ _llint_op_new_array:
dispatch(4)
_llint_op_new_array_with_size:
traceExecution()
callSlowPath(_llint_slow_path_new_array_with_size)
dispatch(3)
_llint_op_new_array_buffer:
traceExecution()
callSlowPath(_llint_slow_path_new_array_buffer)
......
......@@ -77,17 +77,24 @@ bool ArrayConstructor::getOwnPropertyDescriptor(JSObject* object, ExecState* exe
// ------------------------------ Functions ---------------------------
JSObject* constructArrayWithSizeQuirk(ExecState* exec, JSGlobalObject* globalObject, JSValue length)
{
if (!length.isNumber())
return constructArray(exec, globalObject, &length, 1);
uint32_t n = length.toUInt32(exec);
if (n != length.toNumber(exec))
return throwError(exec, createRangeError(exec, ASCIILiteral("Array size is not a small enough positive integer.")));
return constructEmptyArray(exec, globalObject, n);
}
static inline JSObject* constructArrayWithSizeQuirk(ExecState* exec, const ArgList& args)
{
JSGlobalObject* globalObject = asInternalFunction(exec->callee())->globalObject();
// a single numeric argument denotes the array size (!)
if (args.size() == 1 && args.at(0).isNumber()) {