Spread operator should be performing direct "puts" and not triggering setters
https://bugs.webkit.org/show_bug.cgi?id=123047
Reviewed by Geoffrey Garen.
Source/JavaScriptCore:
Add a new opcode -- op_put_by_val_directue -- and make use of it in the spread
to array construct. This required a new PutByValDirect node to be introduced to
the DFG. The current implementation simply changes the slow path function that
is called, but in future this could be made faster as it does not need to check
the prototype chain.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::CodeBlock):
* bytecode/Opcode.h:
(JSC::padOpcodeName):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitDirectPutByVal):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::ArrayNode::emitBytecode):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::::executeEffects):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::getArrayLengthElimination):
(JSC::DFG::CSEPhase::getByValLoadElimination):
(JSC::DFG::CSEPhase::checkStructureElimination):
(JSC::DFG::CSEPhase::structureTransitionWatchpointElimination):
(JSC::DFG::CSEPhase::getByOffsetLoadElimination):
(JSC::DFG::CSEPhase::putByOffsetStoreElimination):
(JSC::DFG::CSEPhase::getPropertyStorageLoadElimination):
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::clobbersWorld):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasArrayMode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
(JSC::DFG::putByVal):
(JSC::DFG::operationPutByValInternal):
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
(JSC::DFG::PredictionPropagationPhase::doDoubleVoting):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compileContiguousPutByVal):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
(JSC::JIT::compileDirectPutByVal):
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emitSlow_op_put_by_val):
(JSC::JIT::privateCompilePutByVal):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emitSlow_op_put_by_val):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
LayoutTests:
Add a new testcase for the setter case. run-javascriptcore-tests hits this with
the llint, baseline, and dfg.
* js/basic-spread-expected.txt:
* js/script-tests/basic-spread.js:
(Array):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@157656 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index d3934fc..5c29d46 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,17 @@
+2013-10-18 Oliver Hunt <oliver@apple.com>
+
+ Spread operator should be performing direct "puts" and not triggering setters
+ https://bugs.webkit.org/show_bug.cgi?id=123047
+
+ Reviewed by Geoffrey Garen.
+
+ Add a new testcase for the setter case. run-javascriptcore-tests hits this with
+ the llint, baseline, and dfg.
+
+ * js/basic-spread-expected.txt:
+ * js/script-tests/basic-spread.js:
+ (Array):
+
2013-10-18 Dean Jackson <dino@apple.com>
Unable to upload <img src="foo.svg"> as WebGL texture
diff --git a/LayoutTests/js/basic-spread-expected.txt b/LayoutTests/js/basic-spread-expected.txt
index 98b774c..f4dabe8 100644
--- a/LayoutTests/js/basic-spread-expected.txt
+++ b/LayoutTests/js/basic-spread-expected.txt
@@ -93,6 +93,7 @@
PASS [,...a,,5] is [,1,2,3,,5]
PASS [...a.keys()] is [0,1,2]
PASS [...a.entries()].join('|') is [[0,1],[1,2],[2,3]].join('|')
+PASS [...a] is [1,2,3]
PASS successfullyParsed is true
TEST COMPLETE
diff --git a/LayoutTests/js/script-tests/basic-spread.js b/LayoutTests/js/script-tests/basic-spread.js
index 19e1062..8f9cf34 100644
--- a/LayoutTests/js/script-tests/basic-spread.js
+++ b/LayoutTests/js/script-tests/basic-spread.js
@@ -58,4 +58,10 @@
shouldBe("[,...a,,5]", "[,1,2,3,,5]")
shouldBe("[...a.keys()]", "[0,1,2]")
shouldBe("[...a.entries()].join('|')", "[[0,1],[1,2],[2,3]].join('|')")
+Array.prototype.__defineSetter__(0, function(){ fail() });
+Array.prototype.__defineSetter__(1, function(){ fail() });
+Array.prototype.__defineSetter__(2, function(){ fail() });
+shouldBe("[...a]", "[1,2,3]")
+
+
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index fd5e478..bc58c06 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,87 @@
+2013-10-18 Oliver Hunt <oliver@apple.com>
+
+ Spread operator should be performing direct "puts" and not triggering setters
+ https://bugs.webkit.org/show_bug.cgi?id=123047
+
+ Reviewed by Geoffrey Garen.
+
+ Add a new opcode -- op_put_by_val_directue -- and make use of it in the spread
+ to array construct. This required a new PutByValDirect node to be introduced to
+ the DFG. The current implementation simply changes the slow path function that
+ is called, but in future this could be made faster as it does not need to check
+ the prototype chain.
+
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode):
+ (JSC::CodeBlock::CodeBlock):
+ * bytecode/Opcode.h:
+ (JSC::padOpcodeName):
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::emitDirectPutByVal):
+ * bytecompiler/BytecodeGenerator.h:
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::ArrayNode::emitBytecode):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::::executeEffects):
+ * dfg/DFGBackwardsPropagationPhase.cpp:
+ (JSC::DFG::BackwardsPropagationPhase::propagate):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCSEPhase.cpp:
+ (JSC::DFG::CSEPhase::getArrayLengthElimination):
+ (JSC::DFG::CSEPhase::getByValLoadElimination):
+ (JSC::DFG::CSEPhase::checkStructureElimination):
+ (JSC::DFG::CSEPhase::structureTransitionWatchpointElimination):
+ (JSC::DFG::CSEPhase::getByOffsetLoadElimination):
+ (JSC::DFG::CSEPhase::putByOffsetStoreElimination):
+ (JSC::DFG::CSEPhase::getPropertyStorageLoadElimination):
+ (JSC::DFG::CSEPhase::performNodeCSE):
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGGraph.h:
+ (JSC::DFG::Graph::clobbersWorld):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::hasArrayMode):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGOperations.cpp:
+ (JSC::DFG::putByVal):
+ (JSC::DFG::operationPutByValInternal):
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ (JSC::DFG::PredictionPropagationPhase::doDoubleVoting):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compileContiguousPutByVal):
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGTypeCheckHoistingPhase.cpp:
+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ (JSC::JIT::privateCompileSlowCases):
+ * jit/JIT.h:
+ (JSC::JIT::compileDirectPutByVal):
+ * jit/JITOperations.cpp:
+ * jit/JITOperations.h:
+ * jit/JITPropertyAccess.cpp:
+ (JSC::JIT::emitSlow_op_put_by_val):
+ (JSC::JIT::privateCompilePutByVal):
+ * jit/JITPropertyAccess32_64.cpp:
+ (JSC::JIT::emitSlow_op_put_by_val):
+ * llint/LLIntSlowPaths.cpp:
+ (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+ * llint/LLIntSlowPaths.h:
+ * llint/LowLevelInterpreter32_64.asm:
+ * llint/LowLevelInterpreter64.asm:
+
2013-10-18 Daniel Bates <dabates@apple.com>
[iOS] Export symbol for VM::sharedInstanceExists()
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index 25d37bb..f27bd19 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1049,6 +1049,15 @@
dumpArrayProfiling(out, it, hasPrintedProfiling);
break;
}
+ case op_put_by_val_direct: {
+ int r0 = (++it)->u.operand;
+ int r1 = (++it)->u.operand;
+ int r2 = (++it)->u.operand;
+ printLocationAndOp(out, exec, location, it, "put_by_val_direct");
+ out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
+ dumpArrayProfiling(out, it, hasPrintedProfiling);
+ break;
+ }
case op_del_by_val: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
@@ -1747,6 +1756,12 @@
instructions[i + opLength - 1] = &m_arrayProfiles[arrayProfileIndex];
break;
}
+ case op_put_by_val_direct: {
+ int arrayProfileIndex = pc[i + opLength - 1].u.operand;
+ m_arrayProfiles[arrayProfileIndex] = ArrayProfile(i);
+ instructions[i + opLength - 1] = &m_arrayProfiles[arrayProfileIndex];
+ break;
+ }
case op_new_array:
case op_new_array_buffer:
diff --git a/Source/JavaScriptCore/bytecode/Opcode.h b/Source/JavaScriptCore/bytecode/Opcode.h
index f0364e2..eb4e7d1 100644
--- a/Source/JavaScriptCore/bytecode/Opcode.h
+++ b/Source/JavaScriptCore/bytecode/Opcode.h
@@ -126,6 +126,7 @@
macro(op_get_argument_by_val, 6) /* must be the same size as op_get_by_val */ \
macro(op_get_by_pname, 7) \
macro(op_put_by_val, 5) \
+ macro(op_put_by_val_direct, 5) \
macro(op_del_by_val, 4) \
macro(op_put_by_index, 4) \
macro(op_put_getter_setter, 5) \
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index ad4c2cf..292f18d 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -1413,6 +1413,17 @@
return value;
}
+RegisterID* BytecodeGenerator::emitDirectPutByVal(RegisterID* base, RegisterID* property, RegisterID* value)
+{
+ UnlinkedArrayProfile arrayProfile = newArrayProfile();
+ emitOpcode(op_put_by_val_direct);
+ instructions().append(base->index());
+ instructions().append(property->index());
+ instructions().append(value->index());
+ instructions().append(arrayProfile);
+ return value;
+}
+
RegisterID* BytecodeGenerator::emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property)
{
emitOpcode(op_del_by_val);
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
index 4c42df1..cadc51a 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
@@ -358,6 +358,7 @@
RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property);
RegisterID* emitGetArgumentByVal(RegisterID* dst, RegisterID* base, RegisterID* property);
RegisterID* emitPutByVal(RegisterID* base, RegisterID* property, RegisterID* value);
+ RegisterID* emitDirectPutByVal(RegisterID* base, RegisterID* property, RegisterID* value);
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);
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index dd37178..c9517a7 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -197,7 +197,7 @@
RefPtr<RegisterID> index = generator.emitLoad(generator.newTemporary(), jsNumber(length));
auto spreader = [this, array, index](BytecodeGenerator& generator, RegisterID* value)
{
- generator.emitPutByVal(array.get(), index.get(), value);
+ generator.emitDirectPutByVal(array.get(), index.get(), value);
generator.emitInc(index.get());
};
for (; n; n = n->next()) {
@@ -207,7 +207,7 @@
SpreadExpressionNode* spread = static_cast<SpreadExpressionNode*>(n->value());
generator.emitEnumeration(spread, spread->expression(), spreader);
} else {
- generator.emitPutByVal(array.get(), index.get(), generator.emitNode(n->value()));
+ generator.emitDirectPutByVal(array.get(), index.get(), generator.emitNode(n->value()));
generator.emitInc(index.get());
}
}
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 7e1bb56..7bc3b8c 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -883,6 +883,7 @@
break;
}
+ case PutByValDirect:
case PutByVal:
case PutByValAlias: {
node->setCanExit(true);
diff --git a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
index a894498..0a2d533 100644
--- a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
@@ -347,7 +347,8 @@
node->child1()->mergeFlags(flags);
break;
}
-
+
+ case PutByValDirect:
case PutByVal: {
m_graph.varArgChild(node, 0)->mergeFlags(NodeBytecodeUsesAsValue);
m_graph.varArgChild(node, 1)->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsInt);
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 1ea7e6d..fb91995 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -2411,6 +2411,7 @@
NEXT_OPCODE(op_get_by_val);
}
+ case op_put_by_val_direct:
case op_put_by_val: {
Node* base = get(VirtualRegister(currentInstruction[1].u.operand));
@@ -2423,7 +2424,7 @@
addVarArgChild(property);
addVarArgChild(value);
addVarArgChild(0); // Leave room for property storage.
- addToGraph(Node::VarArg, PutByVal, OpInfo(arrayMode.asWord()), OpInfo(0));
+ addToGraph(Node::VarArg, opcodeID == op_put_by_val_direct ? PutByValDirect : PutByVal, OpInfo(arrayMode.asWord()), OpInfo(0));
NEXT_OPCODE(op_put_by_val);
}
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 3123dc4..be31f47 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -189,6 +189,7 @@
return node;
break;
+ case PutByValDirect:
case PutByVal:
if (!m_graph.byValIsPure(node))
return 0;
@@ -368,6 +369,8 @@
if (node->child1() == child1 && node->child2() == child2)
return node;
break;
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias: {
if (!m_graph.byValIsPure(node))
@@ -447,7 +450,8 @@
case PutByOffset:
// Setting a property cannot change the structure.
break;
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
if (m_graph.byValIsPure(node)) {
@@ -494,7 +498,8 @@
case PutByOffset:
// Setting a property cannot change the structure.
break;
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
if (m_graph.byValIsPure(node)) {
@@ -612,7 +617,8 @@
return 0;
}
break;
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
if (m_graph.byValIsPure(node)) {
@@ -652,7 +658,8 @@
return 0;
}
break;
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
case GetByVal:
@@ -698,7 +705,8 @@
// storage, so we conservatively assume that it may change the storage
// pointer of any object, including ours.
return 0;
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
if (m_graph.byValIsPure(node)) {
@@ -1252,7 +1260,8 @@
if (m_graph.byValIsPure(node))
setReplacement(getByValLoadElimination(node->child1().node(), node->child2().node()));
break;
-
+
+ case PutByValDirect:
case PutByVal: {
if (cseMode == StoreElimination)
break;
diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
index 358eabc..ba7073f 100644
--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
@@ -121,6 +121,7 @@
case op_nstricteq:
case op_get_by_val:
case op_put_by_val:
+ case op_put_by_val_direct:
case op_get_by_id:
case op_get_by_id_out_of_line:
case op_get_array_length:
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index 245cee9..d968fe6 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -311,7 +311,8 @@
RELEASE_ASSERT_NOT_REACHED();
return;
}
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias: {
ArrayMode mode = node->arrayMode();
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 1a3855c..f77fd59 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -473,7 +473,8 @@
break;
}
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias: {
Edge& child1 = m_graph.varArgChild(node, 0);
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index 084a5a0..2f1a024 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -631,6 +631,7 @@
case CompareEq:
return !isPredictedNumerical(node);
case GetByVal:
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
return !byValIsPure(node);
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 4f45a8f..8cecf8e 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -1071,6 +1071,7 @@
switch (op()) {
case GetIndexedPropertyStorage:
case GetArrayLength:
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
case GetByVal:
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 8d7ef4c..e0399b2 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -140,6 +140,7 @@
/* this must be the directly subsequent property put. Note that PutByVal */\
/* opcodes use VarArgs beause they may have up to 4 children. */\
macro(GetByVal, NodeResultJS | NodeMustGenerate | NodeMightClobber) \
+ macro(PutByValDirect, NodeMustGenerate | NodeHasVarArgs | NodeMightClobber) \
macro(PutByVal, NodeMustGenerate | NodeHasVarArgs | NodeMightClobber) \
macro(PutByValAlias, NodeMustGenerate | NodeHasVarArgs | NodeMightClobber) \
macro(GetById, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index 3486a66..4da0c91 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -61,12 +61,16 @@
namespace JSC { namespace DFG {
-template<bool strict>
+template<bool strict, bool direct>
static inline void putByVal(ExecState* exec, JSValue baseValue, uint32_t index, JSValue value)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
-
+ if (direct) {
+ RELEASE_ASSERT(baseValue.isObject());
+ asObject(baseValue)->putDirectIndex(exec, index, value, 0, strict ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
+ return;
+ }
if (baseValue.isObject()) {
JSObject* object = asObject(baseValue);
if (object->canSetIndexQuickly(index)) {
@@ -81,7 +85,7 @@
baseValue.putByIndex(exec, index, value, strict);
}
-template<bool strict>
+template<bool strict, bool direct>
ALWAYS_INLINE static void JIT_OPERATION operationPutByValInternal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
{
VM* vm = &exec->vm();
@@ -92,7 +96,7 @@
JSValue value = JSValue::decode(encodedValue);
if (LIKELY(property.isUInt32())) {
- putByVal<strict>(exec, baseValue, property.asUInt32(), value);
+ putByVal<strict, direct>(exec, baseValue, property.asUInt32(), value);
return;
}
@@ -100,14 +104,18 @@
double propertyAsDouble = property.asDouble();
uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble);
if (propertyAsDouble == propertyAsUInt32) {
- putByVal<strict>(exec, baseValue, propertyAsUInt32, value);
+ putByVal<strict, direct>(exec, baseValue, propertyAsUInt32, value);
return;
}
}
if (isName(property)) {
PutPropertySlot slot(strict);
- baseValue.put(exec, jsCast<NameInstance*>(property.asCell())->privateName(), value, slot);
+ if (direct) {
+ RELEASE_ASSERT(baseValue.isObject());
+ asObject(baseValue)->putDirect(*vm, jsCast<NameInstance*>(property.asCell())->privateName(), value, slot);
+ } else
+ baseValue.put(exec, jsCast<NameInstance*>(property.asCell())->privateName(), value, slot);
return;
}
@@ -115,7 +123,11 @@
Identifier ident(exec, property.toString(exec)->value(exec));
if (!vm->exception()) {
PutPropertySlot slot(strict);
- baseValue.put(exec, ident, value, slot);
+ if (direct) {
+ RELEASE_ASSERT(baseValue.isObject());
+ asObject(baseValue)->putDirect(*vm, jsCast<NameInstance*>(property.asCell())->privateName(), value, slot);
+ } else
+ baseValue.put(exec, ident, value, slot);
}
}
@@ -350,7 +362,7 @@
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- operationPutByValInternal<true>(exec, encodedBase, encodedProperty, encodedValue);
+ operationPutByValInternal<true, false>(exec, encodedBase, encodedProperty, encodedValue);
}
void JIT_OPERATION operationPutByValNonStrict(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
@@ -358,7 +370,7 @@
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- operationPutByValInternal<false>(exec, encodedBase, encodedProperty, encodedValue);
+ operationPutByValInternal<false, false>(exec, encodedBase, encodedProperty, encodedValue);
}
void JIT_OPERATION operationPutByValCellStrict(ExecState* exec, JSCell* cell, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
@@ -366,7 +378,7 @@
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- operationPutByValInternal<true>(exec, JSValue::encode(cell), encodedProperty, encodedValue);
+ operationPutByValInternal<true, false>(exec, JSValue::encode(cell), encodedProperty, encodedValue);
}
void JIT_OPERATION operationPutByValCellNonStrict(ExecState* exec, JSCell* cell, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
@@ -374,7 +386,7 @@
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- operationPutByValInternal<false>(exec, JSValue::encode(cell), encodedProperty, encodedValue);
+ operationPutByValInternal<false, false>(exec, JSValue::encode(cell), encodedProperty, encodedValue);
}
void JIT_OPERATION operationPutByValBeyondArrayBoundsStrict(ExecState* exec, JSObject* array, int32_t index, EncodedJSValue encodedValue)
@@ -441,6 +453,65 @@
array, exec, Identifier::from(exec, index), jsValue, slot);
}
+void JIT_OPERATION operationPutByValDirectStrict(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ operationPutByValInternal<true, true>(exec, encodedBase, encodedProperty, encodedValue);
+}
+
+void JIT_OPERATION operationPutByValDirectNonStrict(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ operationPutByValInternal<false, true>(exec, encodedBase, encodedProperty, encodedValue);
+}
+
+void JIT_OPERATION operationPutByValDirectCellStrict(ExecState* exec, JSCell* cell, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ operationPutByValInternal<true, true>(exec, JSValue::encode(cell), encodedProperty, encodedValue);
+}
+
+void JIT_OPERATION operationPutByValDirectCellNonStrict(ExecState* exec, JSCell* cell, EncodedJSValue encodedProperty, EncodedJSValue encodedValue)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ operationPutByValInternal<false, true>(exec, JSValue::encode(cell), encodedProperty, encodedValue);
+}
+
+void JIT_OPERATION operationPutByValDirectBeyondArrayBoundsStrict(ExecState* exec, JSObject* array, int32_t index, EncodedJSValue encodedValue)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+ if (index >= 0) {
+ array->putDirectIndex(exec, index, JSValue::decode(encodedValue), 0, PutDirectIndexShouldThrow);
+ return;
+ }
+
+ PutPropertySlot slot(true);
+ array->putDirect(exec->vm(), Identifier::from(exec, index), JSValue::decode(encodedValue), slot);
+}
+
+void JIT_OPERATION operationPutByValDirectBeyondArrayBoundsNonStrict(ExecState* exec, JSObject* array, int32_t index, EncodedJSValue encodedValue)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ if (index >= 0) {
+ array->putDirectIndex(exec, index, JSValue::decode(encodedValue));
+ return;
+ }
+
+ PutPropertySlot slot(false);
+ array->putDirect(exec->vm(), Identifier::from(exec, index), JSValue::decode(encodedValue), slot);
+}
+
EncodedJSValue JIT_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue encodedValue, JSArray* array)
{
VM* vm = &exec->vm();
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h
index 4f1c9ad..735c46e 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.h
+++ b/Source/JavaScriptCore/dfg/DFGOperations.h
@@ -79,6 +79,13 @@
void JIT_OPERATION operationPutByValCellNonStrict(ExecState*, JSCell*, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL;
void JIT_OPERATION operationPutByValBeyondArrayBoundsStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL;
void JIT_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutByValDirectBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutByValDirectStrict(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutByValDirectNonStrict(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutByValDirectCellStrict(ExecState*, JSCell*, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutByValDirectCellNonStrict(ExecState*, JSCell*, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutByValDirectBeyondArrayBoundsStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutByValDirectBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL;
void JIT_OPERATION operationPutDoubleByValBeyondArrayBoundsStrict(ExecState*, JSObject*, int32_t index, double value) WTF_INTERNAL;
void JIT_OPERATION operationPutDoubleByValBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, double value) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationArrayPush(ExecState*, EncodedJSValue encodedValue, JSArray*) WTF_INTERNAL;
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 8248ee5..395578f 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -545,6 +545,7 @@
#ifndef NDEBUG
// These get ignored because they don't return anything.
+ case PutByValDirect:
case PutByVal:
case PutClosureVar:
case Return:
@@ -726,7 +727,8 @@
node->variableAccessData()->vote(VoteValue);
break;
}
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias: {
Edge child1 = m_graph.varArgChild(node, 0);
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index 173e5aa..3468436 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -253,7 +253,8 @@
case GetTypedArrayByteOffset:
return !(state.forNode(node->child1()).m_type & ~(SpecTypedArrayView));
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
return node->arrayMode().modeForPut().alreadyChecked(
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 0d17e51..d8518f3 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -1866,11 +1866,17 @@
storage.use();
if (arrayMode.isOutOfBounds()) {
- addSlowPathGenerator(
- slowPathCall(
+ if (node->op() == PutByValDirect) {
+ addSlowPathGenerator(slowPathCall(
+ slowCase, this,
+ m_jit.codeBlock()->isStrictMode() ? operationPutByValDirectBeyondArrayBoundsStrict : operationPutByValDirectBeyondArrayBoundsNonStrict,
+ NoResult, baseReg, propertyReg, valueTag, valuePayloadReg));
+ } else {
+ addSlowPathGenerator(slowPathCall(
slowCase, this,
m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
NoResult, baseReg, propertyReg, valueTag, valuePayloadReg));
+ }
}
noResult(node, UseChildrenCalledExplicitly);
@@ -2624,6 +2630,7 @@
break;
}
+ case PutByValDirect:
case PutByVal:
case PutByValAlias: {
Edge child1 = m_jit.graph().varArgChild(node, 0);
@@ -2642,7 +2649,7 @@
alreadyHandled = true;
break;
case Array::Generic: {
- ASSERT(node->op() == PutByVal);
+ ASSERT(node->op() == PutByVal || node->op() == PutByValDirect);
SpeculateCellOperand base(this, child1); // Save a register, speculate cell. We'll probably be right.
JSValueOperand property(this, child2);
@@ -2654,7 +2661,10 @@
GPRReg valuePayloadGPR = value.payloadGPR();
flushRegisters();
- callOperation(m_jit.codeBlock()->isStrictMode() ? operationPutByValCellStrict : operationPutByValCellNonStrict, baseGPR, propertyTagGPR, propertyPayloadGPR, valueTagGPR, valuePayloadGPR);
+ if (node->op() == PutByValDirect)
+ callOperation(m_jit.codeBlock()->isStrictMode() ? operationPutByValDirectCellStrict : operationPutByValDirectCellNonStrict, baseGPR, propertyTagGPR, propertyPayloadGPR, valueTagGPR, valuePayloadGPR);
+ else
+ callOperation(m_jit.codeBlock()->isStrictMode() ? operationPutByValCellStrict : operationPutByValCellNonStrict, baseGPR, propertyTagGPR, propertyPayloadGPR, valueTagGPR, valuePayloadGPR);
noResult(node);
alreadyHandled = true;
@@ -2779,11 +2789,17 @@
storage.use();
if (!slowCases.empty()) {
- addSlowPathGenerator(
- slowPathCall(
+ if (node->op() == PutByValDirect) {
+ addSlowPathGenerator(slowPathCall(
+ slowCases, this,
+ m_jit.codeBlock()->isStrictMode() ? operationPutByValDirectBeyondArrayBoundsStrict : operationPutByValDirectBeyondArrayBoundsNonStrict,
+ NoResult, baseReg, propertyReg, valueTagReg, valuePayloadReg));
+ } else {
+ addSlowPathGenerator(slowPathCall(
slowCases, this,
m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
NoResult, baseReg, propertyReg, valueTagReg, valuePayloadReg));
+ }
}
noResult(node, UseChildrenCalledExplicitly);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 5c59c84..da60ccc 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -2915,6 +2915,7 @@
break;
}
+ case PutByValDirect:
case PutByVal:
case PutByValAlias: {
Edge child1 = m_jit.graph().varArgChild(node, 0);
@@ -2942,8 +2943,10 @@
GPRReg arg2GPR = arg2.gpr();
GPRReg arg3GPR = arg3.gpr();
flushRegisters();
-
- callOperation(m_jit.strictModeFor(node->codeOrigin) ? operationPutByValStrict : operationPutByValNonStrict, arg1GPR, arg2GPR, arg3GPR);
+ if (node->op() == PutByValDirect)
+ callOperation(m_jit.strictModeFor(node->codeOrigin) ? operationPutByValDirectStrict : operationPutByValDirectNonStrict, arg1GPR, arg2GPR, arg3GPR);
+ else
+ callOperation(m_jit.strictModeFor(node->codeOrigin) ? operationPutByValStrict : operationPutByValNonStrict, arg1GPR, arg2GPR, arg3GPR);
noResult(node);
alreadyHandled = true;
@@ -3031,11 +3034,17 @@
storage.use();
if (arrayMode.isOutOfBounds()) {
- addSlowPathGenerator(
- slowPathCall(
+ if (node->op() == PutByValDirect) {
+ addSlowPathGenerator(slowPathCall(
+ slowCase, this,
+ m_jit.codeBlock()->isStrictMode() ? operationPutByValDirectBeyondArrayBoundsStrict : operationPutByValDirectBeyondArrayBoundsNonStrict,
+ NoResult, baseReg, propertyReg, valueReg));
+ } else {
+ addSlowPathGenerator(slowPathCall(
slowCase, this,
m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
NoResult, baseReg, propertyReg, valueReg));
+ }
}
noResult(node, UseChildrenCalledExplicitly);
@@ -3119,11 +3128,17 @@
storage.use();
if (!slowCases.empty()) {
- addSlowPathGenerator(
- slowPathCall(
+ if (node->op() == PutByValDirect) {
+ addSlowPathGenerator(slowPathCall(
+ slowCases, this,
+ m_jit.codeBlock()->isStrictMode() ? operationPutByValDirectBeyondArrayBoundsStrict : operationPutByValDirectBeyondArrayBoundsNonStrict,
+ NoResult, baseReg, propertyReg, valueReg));
+ } else {
+ addSlowPathGenerator(slowPathCall(
slowCases, this,
m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
NoResult, baseReg, propertyReg, valueReg));
+ }
}
noResult(node, UseChildrenCalledExplicitly);
diff --git a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
index c2230e3..9483602 100644
--- a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
@@ -249,6 +249,7 @@
case ReallocatePropertyStorage:
case GetButterfly:
case GetByVal:
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
case GetArrayLength:
@@ -350,6 +351,7 @@
case ReallocatePropertyStorage:
case GetButterfly:
case GetByVal:
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
case GetArrayLength:
diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp
index 07750d2..57d8749 100644
--- a/Source/JavaScriptCore/jit/JIT.cpp
+++ b/Source/JavaScriptCore/jit/JIT.cpp
@@ -293,6 +293,7 @@
case op_put_by_id_transition_normal_out_of_line:
DEFINE_OP(op_put_by_id)
DEFINE_OP(op_put_by_index)
+ case op_put_by_val_direct:
DEFINE_OP(op_put_by_val)
DEFINE_OP(op_put_getter_setter)
case op_init_global_const_nop:
@@ -446,6 +447,7 @@
case op_put_by_id_transition_direct_out_of_line:
case op_put_by_id_transition_normal_out_of_line:
DEFINE_SLOWCASE_OP(op_put_by_id)
+ case op_put_by_val_direct:
DEFINE_SLOWCASE_OP(op_put_by_val)
DEFINE_SLOWCASE_OP(op_rshift)
DEFINE_SLOWCASE_OP(op_urshift)
diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h
index 8d847f8..f996178 100644
--- a/Source/JavaScriptCore/jit/JIT.h
+++ b/Source/JavaScriptCore/jit/JIT.h
@@ -323,6 +323,13 @@
jit.m_bytecodeOffset = byValInfo->bytecodeIndex;
jit.privateCompilePutByVal(byValInfo, returnAddress, arrayMode);
}
+
+ static void compileDirectPutByVal(VM* vm, CodeBlock* codeBlock, ByValInfo* byValInfo, ReturnAddressPtr returnAddress, JITArrayMode arrayMode)
+ {
+ JIT jit(vm, codeBlock);
+ jit.m_bytecodeOffset = byValInfo->bytecodeIndex;
+ jit.privateCompilePutByVal(byValInfo, returnAddress, arrayMode);
+ }
static CodeRef compileCTINativeCall(VM* vm, NativeFunction func)
{
diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp
index 53f2a41..1aa4d9e 100644
--- a/Source/JavaScriptCore/jit/JITOperations.cpp
+++ b/Source/JavaScriptCore/jit/JITOperations.cpp
@@ -498,6 +498,22 @@
}
}
+static void directPutByVal(CallFrame* callFrame, JSObject* baseObject, JSValue subscript, JSValue value)
+{
+ if (LIKELY(subscript.isUInt32())) {
+ uint32_t i = subscript.asUInt32();
+ baseObject->putDirectIndex(callFrame, i, value);
+ } else if (isName(subscript)) {
+ PutPropertySlot slot(callFrame->codeBlock()->isStrictMode());
+ baseObject->putDirect(callFrame->vm(), jsCast<NameInstance*>(subscript.asCell())->privateName(), value, slot);
+ } else {
+ Identifier property(callFrame, subscript.toString(callFrame)->value(callFrame));
+ if (!callFrame->vm().exception()) { // Don't put to an object if toString threw an exception.
+ PutPropertySlot slot(callFrame->codeBlock()->isStrictMode());
+ baseObject->putDirect(callFrame->vm(), property, value, slot);
+ }
+ }
+}
void JIT_OPERATION operationPutByVal(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
{
VM& vm = exec->vm();
@@ -544,6 +560,51 @@
putByVal(exec, baseValue, subscript, value);
}
+void JIT_OPERATION operationDirectPutByVal(ExecState* callFrame, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+{
+ VM& vm = callFrame->vm();
+ NativeCallFrameTracer tracer(&vm, callFrame);
+
+ JSValue baseValue = JSValue::decode(encodedBaseValue);
+ JSValue subscript = JSValue::decode(encodedSubscript);
+ JSValue value = JSValue::decode(encodedValue);
+ RELEASE_ASSERT(baseValue.isObject());
+ JSObject* object = asObject(baseValue);
+ if (subscript.isInt32()) {
+ // See if it's worth optimizing at all.
+ bool didOptimize = false;
+
+ unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset();
+ ASSERT(bytecodeOffset);
+ ByValInfo& byValInfo = callFrame->codeBlock()->getByValInfo(bytecodeOffset - 1);
+ ASSERT(!byValInfo.stubRoutine);
+
+ if (hasOptimizableIndexing(object->structure())) {
+ // Attempt to optimize.
+ JITArrayMode arrayMode = jitArrayModeForStructure(object->structure());
+ if (arrayMode != byValInfo.arrayMode) {
+ JIT::compileDirectPutByVal(&vm, callFrame->codeBlock(), &byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode);
+ didOptimize = true;
+ }
+ }
+
+ if (!didOptimize) {
+ // If we take slow path more than 10 times without patching then make sure we
+ // never make that mistake again. Or, if we failed to patch and we have some object
+ // that intercepts indexed get, then don't even wait until 10 times. For cases
+ // where we see non-index-intercepting objects, this gives 10 iterations worth of
+ // opportunity for us to observe that the get_by_val may be polymorphic.
+ if (++byValInfo.slowPathCount >= 10
+ || object->structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) {
+ // Don't ever try to optimize.
+ RepatchBuffer repatchBuffer(callFrame->codeBlock());
+ repatchBuffer.relinkCallerToFunction(ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationDirectPutByValGeneric));
+ }
+ }
+ }
+ directPutByVal(callFrame, object, subscript, value);
+}
+
void JIT_OPERATION operationPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
{
VM& vm = exec->vm();
@@ -556,6 +617,19 @@
putByVal(exec, baseValue, subscript, value);
}
+
+void JIT_OPERATION operationDirectPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ JSValue baseValue = JSValue::decode(encodedBaseValue);
+ JSValue subscript = JSValue::decode(encodedSubscript);
+ JSValue value = JSValue::decode(encodedValue);
+ RELEASE_ASSERT(baseValue.isObject());
+ directPutByVal(exec, asObject(baseValue), subscript, value);
+}
+
EncodedJSValue JIT_OPERATION operationCallEval(ExecState* execCallee)
{
CallFrame* callerFrame = execCallee->callerFrame();
diff --git a/Source/JavaScriptCore/jit/JITOperations.h b/Source/JavaScriptCore/jit/JITOperations.h
index 88af1ba..26d6c1f 100644
--- a/Source/JavaScriptCore/jit/JITOperations.h
+++ b/Source/JavaScriptCore/jit/JITOperations.h
@@ -202,7 +202,9 @@
void JIT_OPERATION operationPutByIdDirectNonStrictBuildList(ExecState*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl*) WTF_INTERNAL;
void JIT_OPERATION operationReallocateStorageAndFinishPut(ExecState*, JSObject*, Structure*, PropertyOffset, EncodedJSValue) WTF_INTERNAL;
void JIT_OPERATION operationPutByVal(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
+void JIT_OPERATION operationDirectPutByVal(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
void JIT_OPERATION operationPutByValGeneric(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
+void JIT_OPERATION operationDirectPutByValGeneric(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationCallEval(ExecState*) WTF_INTERNAL;
char* JIT_OPERATION operationVirtualCall(ExecState*) WTF_INTERNAL;
char* JIT_OPERATION operationLinkCall(ExecState*) WTF_INTERNAL;
diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp
index 6e5c188..ac32fc6 100644
--- a/Source/JavaScriptCore/jit/JITPropertyAccess.cpp
+++ b/Source/JavaScriptCore/jit/JITPropertyAccess.cpp
@@ -475,7 +475,8 @@
emitGetVirtualRegister(property, regT1);
emitGetVirtualRegister(value, regT2);
- Call call = callOperation(operationPutByVal, regT0, regT1, regT2);
+ bool isDirect = m_interpreter->getOpcodeID(currentInstruction->u.opcode) == op_put_by_val_direct;
+ Call call = callOperation(isDirect ? operationDirectPutByVal : operationPutByVal, regT0, regT1, regT2);
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call;
@@ -1030,13 +1031,20 @@
patchBuffer.link(done, byValInfo->badTypeJump.labelAtOffset(byValInfo->badTypeJumpToDone));
- byValInfo->stubRoutine = FINALIZE_CODE_FOR_STUB(
- patchBuffer,
- ("Baseline put_by_val stub for %s, return point %p", toCString(*m_codeBlock).data(), returnAddress.value()));
-
+ bool isDirect = m_interpreter->getOpcodeID(currentInstruction->u.opcode) == op_put_by_val_direct;
+ if (!isDirect) {
+ byValInfo->stubRoutine = FINALIZE_CODE_FOR_STUB(
+ patchBuffer,
+ ("Baseline put_by_val stub for %s, return point %p", toCString(*m_codeBlock).data(), returnAddress.value()));
+
+ } else {
+ byValInfo->stubRoutine = FINALIZE_CODE_FOR_STUB(
+ patchBuffer,
+ ("Baseline put_by_val_direct stub for %s, return point %p", toCString(*m_codeBlock).data(), returnAddress.value()));
+ }
RepatchBuffer repatchBuffer(m_codeBlock);
repatchBuffer.relink(byValInfo->badTypeJump, CodeLocationLabel(byValInfo->stubRoutine->code().code()));
- repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(operationPutByValGeneric));
+ repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(isDirect ? operationDirectPutByValGeneric : operationPutByValGeneric));
}
JIT::JumpList JIT::emitIntTypedArrayGetByVal(Instruction*, PatchableJump& badType, TypedArrayType type)
diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
index b82c972..fc4e359 100644
--- a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
@@ -435,6 +435,8 @@
skipProfiling.link(this);
Label slowPath = label();
+
+ bool isDirect = m_interpreter->getOpcodeID(currentInstruction->u.opcode) == op_put_by_val_direct;
#if CPU(X86)
// FIXME: We only have 5 temp registers, but need 6 to make this call, therefore we materialize
@@ -451,14 +453,14 @@
emitLoad(value, regT0, regT1);
addCallArgument(regT1);
addCallArgument(regT0);
- Call call = appendCallWithExceptionCheck(operationPutByVal);
+ Call call = appendCallWithExceptionCheck(isDirect ? operationDirectPutByVal : operationPutByVal);
#else
// The register selection below is chosen to reduce register swapping on ARM.
// Swapping shouldn't happen on other platforms.
emitLoad(base, regT2, regT1);
emitLoad(property, regT3, regT0);
emitLoad(value, regT5, regT4);
- Call call = callOperation(operationPutByVal, regT2, regT1, regT3, regT0, regT5, regT4);
+ Call call = callOperation(isDirect ? operationDirectPutByVal : operationPutByVal, regT2, regT1, regT3, regT0, regT5, regT4);
#endif
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
index 38dd2eb..66fb029 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
@@ -745,6 +745,31 @@
LLINT_END();
}
+LLINT_SLOW_PATH_DECL(slow_path_put_by_val_direct)
+{
+ LLINT_BEGIN();
+
+ JSValue baseValue = LLINT_OP_C(1).jsValue();
+ JSValue subscript = LLINT_OP_C(2).jsValue();
+ JSValue value = LLINT_OP_C(3).jsValue();
+ RELEASE_ASSERT(baseValue.isObject());
+ JSObject* baseObject = asObject(baseValue);
+ if (LIKELY(subscript.isUInt32())) {
+ uint32_t i = subscript.asUInt32();
+ baseObject->putDirectIndex(exec, i, value);
+ } else if (isName(subscript)) {
+ PutPropertySlot slot(exec->codeBlock()->isStrictMode());
+ baseObject->putDirect(exec->vm(), jsCast<NameInstance*>(subscript.asCell())->privateName(), value, slot);
+ } else {
+ Identifier property(exec, subscript.toString(exec)->value(exec));
+ if (!exec->vm().exception()) { // Don't put to an object if toString threw an exception.
+ PutPropertySlot slot(exec->codeBlock()->isStrictMode());
+ baseObject->putDirect(exec->vm(), property, value, slot);
+ }
+ }
+ LLINT_END();
+}
+
LLINT_SLOW_PATH_DECL(slow_path_del_by_val)
{
LLINT_BEGIN();
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.h b/Source/JavaScriptCore/llint/LLIntSlowPaths.h
index 9beacd0..268665d 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.h
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.h
@@ -79,6 +79,7 @@
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_argument_by_val);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_pname);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_val);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_val_direct);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_del_by_val);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_index);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_getter_setter);
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
index 694a279..e68bcbc 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
@@ -1300,7 +1300,7 @@
jmp .storeResult
end
-_llint_op_put_by_val:
+macro putByVal(holeCheck, slowPath)
traceExecution()
loadi 4[PC], t0
loadConstantOrVariablePayload(t0, CellTag, t1, .opPutByValSlow)
@@ -1351,7 +1351,7 @@
.opPutByValNotContiguous:
bineq t2, ArrayStorageShape, .opPutByValSlow
biaeq t3, -sizeof IndexingHeader + IndexingHeader::u.lengths.vectorLength[t0], .opPutByValOutOfBounds
- bieq ArrayStorage::m_vector + TagOffset[t0, t3, 8], EmptyValueTag, .opPutByValArrayStorageEmpty
+ holeCheck(ArrayStorage::m_vector + TagOffset[t0, t3, 8], .opPutByValArrayStorageEmpty)
.opPutByValArrayStorageStoreResult:
loadi 12[PC], t2
loadConstantOrVariable2Reg(t2, t1, t2)
@@ -1377,9 +1377,18 @@
storeb 1, ArrayProfile::m_outOfBounds[t0]
end
.opPutByValSlow:
- callSlowPath(_llint_slow_path_put_by_val)
+ callSlowPath(slowPath)
dispatch(5)
+end
+_llint_op_put_by_val:
+ putByVal(macro(addr, slowPath)
+ bieq addr, EmptyValueTag, slowPath
+ end, _llint_slow_path_put_by_val)
+
+_llint_op_put_by_val_direct:
+ putByVal(macro(addr, slowPath)
+ end, _llint_slow_path_put_by_val_direct)
_llint_op_jmp:
traceExecution()
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
index 7bee0ac..52c3e60 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
@@ -1163,7 +1163,7 @@
jmp .storeResult
end
-_llint_op_put_by_val:
+macro putByVal(holeCheck, slowPath)
traceExecution()
loadisFromInstruction(1, t0)
loadConstantOrVariableCell(t0, t1, .opPutByValSlow)
@@ -1211,7 +1211,7 @@
.opPutByValNotContiguous:
bineq t2, ArrayStorageShape, .opPutByValSlow
biaeq t3, -sizeof IndexingHeader + IndexingHeader::u.lengths.vectorLength[t0], .opPutByValOutOfBounds
- btqz ArrayStorage::m_vector[t0, t3, 8], .opPutByValArrayStorageEmpty
+ holeCheck(ArrayStorage::m_vector[t0, t3, 8], .opPutByValArrayStorageEmpty)
.opPutByValArrayStorageStoreResult:
loadisFromInstruction(3, t2)
loadConstantOrVariable(t2, t1)
@@ -1236,8 +1236,18 @@
storeb 1, ArrayProfile::m_outOfBounds[t0]
end
.opPutByValSlow:
- callSlowPath(_llint_slow_path_put_by_val)
+ callSlowPath(slowPath)
dispatch(5)
+end
+
+_llint_op_put_by_val:
+ putByVal(macro(slot, slowPath)
+ btqz slot, slowPath
+ end, _llint_slow_path_put_by_val)
+
+_llint_op_put_by_val_direct:
+ putByVal(macro(slot, slowPath)
+ end, _llint_slow_path_put_by_val_direct)
_llint_op_jmp: