DFG::operationNewArray is unnecessarily slow, and may use the wrong array
prototype when inlined
https://bugs.webkit.org/show_bug.cgi?id=89821
Source/JavaScriptCore:
Reviewed by Geoffrey Garen.
Fixes all array allocations to use the right structure, and hence the right prototype. Adds
inlining of new Array(...) with a non-zero number of arguments. Optimizes allocations of
empty arrays.
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
* dfg/DFGCCallHelpers.h:
(JSC::DFG::CCallHelpers::setupArgumentsWithExecState):
(CCallHelpers):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* runtime/JSArray.h:
(JSC):
(JSC::constructArray):
* runtime/JSGlobalObject.h:
(JSC):
(JSC::constructArray):
LayoutTests:
Rubber stamped by Geoffrey Garen.
* fast/js/dfg-cross-global-object-inline-new-array-expected.txt: Added.
* fast/js/dfg-cross-global-object-inline-new-array-literal-expected.txt: Added.
* fast/js/dfg-cross-global-object-inline-new-array-literal-with-variables-expected.txt: Added.
* fast/js/dfg-cross-global-object-inline-new-array-literal-with-variables.html: Added.
* fast/js/dfg-cross-global-object-inline-new-array-literal.html: Added.
* fast/js/dfg-cross-global-object-inline-new-array-with-elements-expected.txt: Added.
* fast/js/dfg-cross-global-object-inline-new-array-with-elements.html: Added.
* fast/js/dfg-cross-global-object-inline-new-array-with-size-expected.txt: Added.
* fast/js/dfg-cross-global-object-inline-new-array-with-size.html: Added.
* fast/js/dfg-cross-global-object-inline-new-array.html: Added.
* fast/js/script-tests/cross-global-object-inline-global-var.js:
(done):
* fast/js/script-tests/dfg-cross-global-object-inline-new-array-literal-with-variables.js: Added.
(foo):
(done):
(doit):
* fast/js/script-tests/dfg-cross-global-object-inline-new-array-literal.js: Added.
(foo):
(done):
(doit):
* fast/js/script-tests/dfg-cross-global-object-inline-new-array-with-elements.js: Added.
(foo):
(done):
(doit):
* fast/js/script-tests/dfg-cross-global-object-inline-new-array-with-size.js: Added.
(foo):
(done):
(doit):
* fast/js/script-tests/dfg-cross-global-object-inline-new-array.js: Added.
(foo):
(done):
(doit):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@121280 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 72aa427..4225402 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,41 @@
+2012-06-25 Filip Pizlo <fpizlo@apple.com>
+
+ DFG::operationNewArray is unnecessarily slow, and may use the wrong array
+ prototype when inlined
+ https://bugs.webkit.org/show_bug.cgi?id=89821
+
+ Reviewed by Geoffrey Garen.
+
+ Fixes all array allocations to use the right structure, and hence the right prototype. Adds
+ inlining of new Array(...) with a non-zero number of arguments. Optimizes allocations of
+ empty arrays.
+
+ * dfg/DFGAbstractState.cpp:
+ (JSC::DFG::AbstractState::execute):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
+ * dfg/DFGCCallHelpers.h:
+ (JSC::DFG::CCallHelpers::setupArgumentsWithExecState):
+ (CCallHelpers):
+ * dfg/DFGNodeType.h:
+ (DFG):
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * runtime/JSArray.h:
+ (JSC):
+ (JSC::constructArray):
+ * runtime/JSGlobalObject.h:
+ (JSC):
+ (JSC::constructArray):
+
2012-06-26 Filip Pizlo <fpizlo@apple.com>
New fast/js/dfg-store-unexpected-value-into-argument-and-osr-exit.html fails on 32 bit
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index b4205ef..c2d49f7 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -1141,13 +1141,19 @@
case NewArray:
case NewArrayBuffer:
node.setCanExit(false);
- forNode(nodeIndex).set(m_codeBlock->globalObject()->arrayStructure());
+ forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructure());
+ m_haveStructures = true;
+ break;
+
+ case NewArrayWithSize:
+ speculateInt32Unary(node);
+ forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructure());
m_haveStructures = true;
break;
case NewRegexp:
node.setCanExit(false);
- forNode(nodeIndex).set(m_codeBlock->globalObject()->regExpStructure());
+ forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->regExpStructure());
m_haveStructures = true;
break;
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 7561197..cdb0b63 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -1606,15 +1606,19 @@
// we know about is small enough, that having just a linear cascade of if statements
// is good enough.
- UNUSED_PARAM(registerOffset); // Remove this once we do more things to the arguments.
UNUSED_PARAM(prediction); // Remove this once we do more things.
UNUSED_PARAM(kind); // Remove this once we do more things.
if (function->classInfo() == &ArrayConstructor::s_info) {
- // We could handle this but don't for now.
- if (argumentCountIncludingThis != 1)
- return false;
+ if (argumentCountIncludingThis == 2) {
+ setIntrinsicResult(
+ usesResult, resultOperand,
+ addToGraph(NewArrayWithSize, get(registerOffset + argumentToOperand(1))));
+ return true;
+ }
+ for (int i = 1; i < argumentCountIncludingThis; ++i)
+ addVarArgChild(get(registerOffset + argumentToOperand(i)));
setIntrinsicResult(
usesResult, resultOperand,
addToGraph(Node::VarArg, NewArray, OpInfo(0), OpInfo(0)));
diff --git a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h
index 4cacd45..b602908 100644
--- a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h
+++ b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h
@@ -160,6 +160,14 @@
addCallArgument(arg2);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, GPRReg arg2)
+ {
+ resetCallArguments();
+ addCallArgument(GPRInfo::callFrameRegister);
+ addCallArgument(arg1);
+ addCallArgument(arg2);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, TrustedImm32 arg2)
{
resetCallArguments();
@@ -203,6 +211,15 @@
addCallArgument(arg3);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, TrustedImmPtr arg2, TrustedImmPtr arg3)
+ {
+ resetCallArguments();
+ addCallArgument(GPRInfo::callFrameRegister);
+ addCallArgument(arg1);
+ addCallArgument(arg2);
+ addCallArgument(arg3);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImmPtr arg2, TrustedImmPtr arg3)
{
resetCallArguments();
@@ -597,6 +614,14 @@
move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, TrustedImmPtr arg2, TrustedImmPtr arg3)
+ {
+ move(arg1, GPRInfo::argumentGPR1);
+ move(arg2, GPRInfo::argumentGPR2);
+ move(arg3, GPRInfo::argumentGPR3);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, TrustedImm32 arg2, TrustedImm32 arg3)
{
move(arg1, GPRInfo::argumentGPR1);
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 4671f6b..db798d0 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -184,6 +184,7 @@
/* Allocations. */\
macro(NewObject, NodeResultJS) \
macro(NewArray, NodeResultJS | NodeHasVarArgs) \
+ macro(NewArrayWithSize, NodeResultJS) \
macro(NewArrayBuffer, NodeResultJS) \
macro(NewRegexp, NodeResultJS) \
\
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index 3ece917..11362f4 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -1005,12 +1005,22 @@
return JSValue::encode(jsString(exec, static_cast<Register*>(buffer), size));
}
-EncodedJSValue DFG_OPERATION operationNewArray(ExecState* exec, void* buffer, size_t size)
+EncodedJSValue DFG_OPERATION operationNewArray(ExecState* exec, Structure* arrayStructure, void* buffer, size_t size)
{
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
- return JSValue::encode(constructArray(exec, static_cast<JSValue*>(buffer), size));
+ return JSValue::encode(constructArray(exec, arrayStructure, static_cast<JSValue*>(buffer), size));
+}
+
+EncodedJSValue DFG_OPERATION operationNewEmptyArray(ExecState* exec, Structure* arrayStructure)
+{
+ return JSValue::encode(JSArray::create(exec->globalData(), arrayStructure));
+}
+
+EncodedJSValue DFG_OPERATION operationNewArrayWithSize(ExecState* exec, Structure* arrayStructure, int32_t size)
+{
+ return JSValue::encode(JSArray::create(exec->globalData(), arrayStructure, size));
}
EncodedJSValue DFG_OPERATION operationNewArrayBuffer(ExecState* exec, size_t start, size_t size)
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h
index 7477ab2..3c85ee7 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.h
+++ b/Source/JavaScriptCore/dfg/DFGOperations.h
@@ -76,6 +76,9 @@
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EPP)(ExecState*, void*, void*);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EPS)(ExecState*, void*, size_t);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ESS)(ExecState*, size_t, size_t);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ESt)(ExecState*, Structure*);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EStI)(ExecState*, Structure*, int32_t);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EStPS)(ExecState*, Structure*, void*, size_t);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EZ)(ExecState*, int32_t);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EZIcfZ)(ExecState*, int32_t, InlineCallFrame*, int32_t);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EZZ)(ExecState*, int32_t, int32_t);
@@ -124,8 +127,10 @@
EncodedJSValue DFG_OPERATION operationResolveGlobal(ExecState*, GlobalResolveInfo*, JSGlobalObject*, Identifier*) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationToPrimitive(ExecState*, EncodedJSValue) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationStrCat(ExecState*, void*, size_t) WTF_INTERNAL;
-EncodedJSValue DFG_OPERATION operationNewArray(ExecState*, void*, size_t) WTF_INTERNAL;
+EncodedJSValue DFG_OPERATION operationNewArray(ExecState*, Structure*, void*, size_t) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationNewArrayBuffer(ExecState*, size_t, size_t) WTF_INTERNAL;
+EncodedJSValue DFG_OPERATION operationNewEmptyArray(ExecState*, Structure*) WTF_INTERNAL;
+EncodedJSValue DFG_OPERATION operationNewArrayWithSize(ExecState*, Structure*, int32_t) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationNewRegexp(ExecState*, void*) WTF_INTERNAL;
void DFG_OPERATION operationPutByValStrict(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL;
void DFG_OPERATION operationPutByValNonStrict(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL;
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index bcb79a9..0bd81ec 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -535,6 +535,12 @@
break;
}
+ case NewArrayWithSize: {
+ changed |= setPrediction(SpecArray);
+ changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ break;
+ }
+
case NewArrayBuffer: {
changed |= setPrediction(SpecArray);
break;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 6c6948b..67a22b7 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -1244,6 +1244,21 @@
m_jit.setupArgumentsWithExecState(arg1);
return appendCallWithExceptionCheckSetResult(operation, result);
}
+ JITCompiler::Call callOperation(J_DFGOperation_ESt operation, GPRReg result, Structure* structure)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure));
+ return appendCallWithExceptionCheckSetResult(operation, result);
+ }
+ JITCompiler::Call callOperation(J_DFGOperation_EStI operation, GPRReg result, Structure* structure, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), arg2);
+ return appendCallWithExceptionCheckSetResult(operation, result);
+ }
+ JITCompiler::Call callOperation(J_DFGOperation_EStPS operation, GPRReg result, Structure* structure, void* pointer, size_t size)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), TrustedImmPtr(pointer), TrustedImmPtr(size));
+ return appendCallWithExceptionCheckSetResult(operation, result);
+ }
JITCompiler::Call callOperation(J_DFGOperation_EPS operation, GPRReg result, void* pointer, size_t size)
{
m_jit.setupArgumentsWithExecState(TrustedImmPtr(pointer), TrustedImmPtr(size));
@@ -1502,6 +1517,21 @@
m_jit.setupArgumentsWithExecState(arg1);
return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
}
+ JITCompiler::Call callOperation(J_DFGOperation_ESt operation, GPRReg resultTag, GPRReg resultPayload, Structure* structure)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure));
+ return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
+ }
+ JITCompiler::Call callOperation(J_DFGOperation_EStI operation, GPRReg resultTag, GPRReg resultPayload, Structure* structure, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), arg2);
+ return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
+ }
+ JITCompiler::Call callOperation(J_DFGOperation_EStPS operation, GPRReg resultTag, GPRReg resultPayload, Structure* structure, void* pointer, size_t size)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure), TrustedImmPtr(pointer), TrustedImmPtr(size));
+ return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
+ }
JITCompiler::Call callOperation(J_DFGOperation_EPS operation, GPRReg resultTag, GPRReg resultPayload, void* pointer, size_t size)
{
m_jit.setupArgumentsWithExecState(TrustedImmPtr(pointer), TrustedImmPtr(size));
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index e007a6e..05609ba 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -2975,25 +2975,7 @@
break;
}
- case StrCat:
- case NewArray: {
- // We really don't want to grow the register file just to do a StrCat or NewArray.
- // Say we have 50 functions on the stack that all have a StrCat in them that has
- // upwards of 10 operands. In the DFG this would mean that each one gets
- // some random virtual register, and then to do the StrCat we'd need a second
- // span of 10 operands just to have somewhere to copy the 10 operands to, where
- // they'd be contiguous and we could easily tell the C code how to find them.
- // Ugly! So instead we use the scratchBuffer infrastructure in JSGlobalData. That
- // way, those 50 functions will share the same scratchBuffer for offloading their
- // StrCat operands. It's about as good as we can do, unless we start doing
- // virtual register coalescing to ensure that operands to StrCat get spilled
- // in exactly the place where StrCat wants them, or else have the StrCat
- // refer to those operands' SetLocal instructions to force them to spill in
- // the right place. Basically, any way you cut it, the current approach
- // probably has the best balance of performance and sensibility in the sense
- // that it does not increase the complexity of the DFG JIT just to make StrCat
- // fast and pretty.
-
+ case StrCat: {
size_t scratchSize = sizeof(EncodedJSValue) * node.numChildren();
ScratchBuffer* scratchBuffer = m_jit.globalData()->scratchBufferForSize(scratchSize);
EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
@@ -3021,7 +3003,7 @@
GPRResult resultPayload(this);
GPRResult2 resultTag(this);
- callOperation(op == StrCat ? operationStrCat : operationNewArray, resultTag.gpr(), resultPayload.gpr(), static_cast<void *>(buffer), node.numChildren());
+ callOperation(operationStrCat, resultTag.gpr(), resultPayload.gpr(), static_cast<void *>(buffer), node.numChildren());
if (scratchSize) {
GPRTemporary scratch(this);
@@ -3035,6 +3017,75 @@
break;
}
+ case NewArray: {
+ if (!node.numChildren()) {
+ flushRegisters();
+ GPRResult result(this);
+ GPRResult2 resultTagIgnored(this);
+ callOperation(
+ operationNewEmptyArray, resultTagIgnored.gpr(), result.gpr(),
+ m_jit.graph().globalObjectFor(node.codeOrigin)->arrayStructure());
+ cellResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ size_t scratchSize = sizeof(EncodedJSValue) * node.numChildren();
+ ScratchBuffer* scratchBuffer = m_jit.globalData()->scratchBufferForSize(scratchSize);
+ EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
+
+ for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) {
+ JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]);
+ GPRReg opTagGPR = operand.tagGPR();
+ GPRReg opPayloadGPR = operand.payloadGPR();
+ operand.use();
+
+ m_jit.store32(opTagGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
+ m_jit.store32(opPayloadGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
+ }
+
+ flushRegisters();
+
+ if (scratchSize) {
+ GPRTemporary scratch(this);
+
+ // Tell GC mark phase how much of the scratch buffer is active during call.
+ m_jit.move(TrustedImmPtr(scratchBuffer->activeLengthPtr()), scratch.gpr());
+ m_jit.storePtr(TrustedImmPtr(scratchSize), scratch.gpr());
+ }
+
+ GPRResult resultPayload(this);
+ GPRResult2 resultTag(this);
+
+ callOperation(
+ operationNewArray, resultTag.gpr(), resultPayload.gpr(),
+ m_jit.graph().globalObjectFor(node.codeOrigin)->arrayStructure(),
+ static_cast<void *>(buffer), node.numChildren());
+
+ if (scratchSize) {
+ GPRTemporary scratch(this);
+
+ m_jit.move(TrustedImmPtr(scratchBuffer->activeLengthPtr()), scratch.gpr());
+ m_jit.storePtr(TrustedImmPtr(0), scratch.gpr());
+ }
+
+ // FIXME: make the callOperation above explicitly return a cell result, or jitAssert the tag is a cell tag.
+ cellResult(resultPayload.gpr(), m_compileIndex, UseChildrenCalledExplicitly);
+ break;
+ }
+
+ case NewArrayWithSize: {
+ SpeculateStrictInt32Operand size(this, node.child1());
+ GPRReg sizeGPR = size.gpr();
+ flushRegisters();
+ GPRResult result(this);
+ GPRResult2 resultTagIgnored(this);
+ callOperation(
+ operationNewArrayWithSize, resultTagIgnored.gpr(), result.gpr(),
+ m_jit.graph().globalObjectFor(node.codeOrigin)->arrayStructure(), sizeGPR);
+ cellResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
case NewArrayBuffer: {
flushRegisters();
GPRResult resultPayload(this);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index a6c2835..215f801 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -3035,25 +3035,17 @@
break;
}
- case StrCat:
case NewArray: {
- // We really don't want to grow the register file just to do a StrCat or NewArray.
- // Say we have 50 functions on the stack that all have a StrCat in them that has
- // upwards of 10 operands. In the DFG this would mean that each one gets
- // some random virtual register, and then to do the StrCat we'd need a second
- // span of 10 operands just to have somewhere to copy the 10 operands to, where
- // they'd be contiguous and we could easily tell the C code how to find them.
- // Ugly! So instead we use the scratchBuffer infrastructure in JSGlobalData. That
- // way, those 50 functions will share the same scratchBuffer for offloading their
- // StrCat operands. It's about as good as we can do, unless we start doing
- // virtual register coalescing to ensure that operands to StrCat get spilled
- // in exactly the place where StrCat wants them, or else have the StrCat
- // refer to those operands' SetLocal instructions to force them to spill in
- // the right place. Basically, any way you cut it, the current approach
- // probably has the best balance of performance and sensibility in the sense
- // that it does not increase the complexity of the DFG JIT just to make StrCat
- // fast and pretty.
-
+ if (!node.numChildren()) {
+ flushRegisters();
+ GPRResult result(this);
+ callOperation(
+ operationNewEmptyArray, result.gpr(),
+ m_jit.graph().globalObjectFor(node.codeOrigin)->arrayStructure());
+ cellResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
size_t scratchSize = sizeof(EncodedJSValue) * node.numChildren();
ScratchBuffer* scratchBuffer = m_jit.globalData()->scratchBufferForSize(scratchSize);
EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
@@ -3078,7 +3070,58 @@
GPRResult result(this);
- callOperation(op == StrCat ? operationStrCat : operationNewArray, result.gpr(), static_cast<void *>(buffer), node.numChildren());
+ callOperation(
+ operationNewArray, result.gpr(),
+ m_jit.graph().globalObjectFor(node.codeOrigin)->arrayStructure(),
+ static_cast<void*>(buffer), node.numChildren());
+
+ if (scratchSize) {
+ GPRTemporary scratch(this);
+
+ m_jit.move(TrustedImmPtr(scratchBuffer->activeLengthPtr()), scratch.gpr());
+ m_jit.storePtr(TrustedImmPtr(0), scratch.gpr());
+ }
+
+ cellResult(result.gpr(), m_compileIndex, UseChildrenCalledExplicitly);
+ break;
+ }
+
+ case NewArrayWithSize: {
+ SpeculateStrictInt32Operand size(this, node.child1());
+ GPRReg sizeGPR = size.gpr();
+ flushRegisters();
+ GPRResult result(this);
+ callOperation(operationNewArrayWithSize, result.gpr(), m_jit.graph().globalObjectFor(node.codeOrigin)->arrayStructure(), sizeGPR);
+ cellResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case StrCat: {
+ size_t scratchSize = sizeof(EncodedJSValue) * node.numChildren();
+ ScratchBuffer* scratchBuffer = m_jit.globalData()->scratchBufferForSize(scratchSize);
+ EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
+
+ for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) {
+ JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]);
+ GPRReg opGPR = operand.gpr();
+ operand.use();
+
+ m_jit.storePtr(opGPR, buffer + operandIdx);
+ }
+
+ flushRegisters();
+
+ if (scratchSize) {
+ GPRTemporary scratch(this);
+
+ // Tell GC mark phase how much of the scratch buffer is active during call.
+ m_jit.move(TrustedImmPtr(scratchBuffer->activeLengthPtr()), scratch.gpr());
+ m_jit.storePtr(TrustedImmPtr(scratchSize), scratch.gpr());
+ }
+
+ GPRResult result(this);
+
+ callOperation(operationStrCat, result.gpr(), static_cast<void *>(buffer), node.numChildren());
if (scratchSize) {
GPRTemporary scratch(this);
diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h
index c1a3a63..52c5913 100644
--- a/Source/JavaScriptCore/runtime/JSArray.h
+++ b/Source/JavaScriptCore/runtime/JSArray.h
@@ -380,7 +380,42 @@
return size;
}
+
+ inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const ArgList& values)
+ {
+ JSGlobalData& globalData = exec->globalData();
+ unsigned length = values.size();
+ JSArray* array = JSArray::tryCreateUninitialized(globalData, arrayStructure, length);
+
+ // FIXME: we should probably throw an out of memory error here, but
+ // when making this change we should check that all clients of this
+ // function will correctly handle an exception being thrown from here.
+ if (!array)
+ CRASH();
+
+ for (unsigned i = 0; i < length; ++i)
+ array->initializeIndex(globalData, i, values.at(i));
+ array->completeInitialization(length);
+ return array;
+ }
- } // namespace JSC
+ inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length)
+ {
+ JSGlobalData& globalData = exec->globalData();
+ JSArray* array = JSArray::tryCreateUninitialized(globalData, arrayStructure, length);
+
+ // FIXME: we should probably throw an out of memory error here, but
+ // when making this change we should check that all clients of this
+ // function will correctly handle an exception being thrown from here.
+ if (!array)
+ CRASH();
+
+ for (unsigned i = 0; i < length; ++i)
+ array->initializeIndex(globalData, i, values[i]);
+ array->completeInitialization(length);
+ return array;
+ }
+
+} // namespace JSC
#endif // JSArray_h
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h
index 8fec633..1dcfc63 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h
@@ -445,23 +445,10 @@
{
return constructEmptyArray(exec, exec->lexicalGlobalObject(), initialLength);
}
-
+
inline JSArray* constructArray(ExecState* exec, JSGlobalObject* globalObject, const ArgList& values)
{
- JSGlobalData& globalData = exec->globalData();
- unsigned length = values.size();
- JSArray* array = JSArray::tryCreateUninitialized(globalData, globalObject->arrayStructure(), length);
-
- // FIXME: we should probably throw an out of memory error here, but
- // when making this change we should check that all clients of this
- // function will correctly handle an exception being thrown from here.
- if (!array)
- CRASH();
-
- for (unsigned i = 0; i < length; ++i)
- array->initializeIndex(globalData, i, values.at(i));
- array->completeInitialization(length);
- return array;
+ return constructArray(exec, globalObject->arrayStructure(), values);
}
inline JSArray* constructArray(ExecState* exec, const ArgList& values)
@@ -471,19 +458,7 @@
inline JSArray* constructArray(ExecState* exec, JSGlobalObject* globalObject, const JSValue* values, unsigned length)
{
- JSGlobalData& globalData = exec->globalData();
- JSArray* array = JSArray::tryCreateUninitialized(globalData, globalObject->arrayStructure(), length);
-
- // FIXME: we should probably throw an out of memory error here, but
- // when making this change we should check that all clients of this
- // function will correctly handle an exception being thrown from here.
- if (!array)
- CRASH();
-
- for (unsigned i = 0; i < length; ++i)
- array->initializeIndex(globalData, i, values[i]);
- array->completeInitialization(length);
- return array;
+ return constructArray(exec, globalObject->arrayStructure(), values, length);
}
inline JSArray* constructArray(ExecState* exec, const JSValue* values, unsigned length)