Avoid reloading storage pointer for indexed properties unnecessarily
https://bugs.webkit.org/show_bug.cgi?id=74136
Reviewed by Filip Pizlo.
Add a node to represent loading property storage for indexed properties.
This allows us to reduce code generated for sequential access of arrays,
strings, etc. This results in up to 5% improvement in code that is
very heavy on indexed reads, such as matrix operations in typed arrays
and 20% faster on microbenchmarks.
Currently this is only supported by GetByVal and other similar indexed reads.
* bytecode/PredictedType.h:
(JSC::isFixedIndexedStorageObjectPrediction):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsic):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGNode.h:
* dfg/DFGPropagator.cpp:
(JSC::DFG::Propagator::propagateNodePredictions):
(JSC::DFG::Propagator::fixupNode):
(JSC::DFG::Propagator::getIndexedPropertyStorageLoadElimination):
(JSC::DFG::Propagator::performNodeCSE):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetCharCodeAt):
(JSC::DFG::SpeculativeJIT::compileGetByValOnString):
(JSC::DFG::SpeculativeJIT::compileGetByValOnByteArray):
(JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray):
(JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray):
(JSC::DFG::SpeculativeJIT::compileGetIndexedPropertyStorage):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@102442 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 5bc0f7d..f9ce406 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,44 @@
+2011-12-09 Oliver Hunt <oliver@apple.com>
+
+ Avoid reloading storage pointer for indexed properties unnecessarily
+ https://bugs.webkit.org/show_bug.cgi?id=74136
+
+ Reviewed by Filip Pizlo.
+
+ Add a node to represent loading property storage for indexed properties.
+ This allows us to reduce code generated for sequential access of arrays,
+ strings, etc. This results in up to 5% improvement in code that is
+ very heavy on indexed reads, such as matrix operations in typed arrays
+ and 20% faster on microbenchmarks.
+
+ Currently this is only supported by GetByVal and other similar indexed reads.
+
+ * bytecode/PredictedType.h:
+ (JSC::isFixedIndexedStorageObjectPrediction):
+ * dfg/DFGAbstractState.cpp:
+ (JSC::DFG::AbstractState::execute):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleIntrinsic):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGNode.h:
+ * dfg/DFGPropagator.cpp:
+ (JSC::DFG::Propagator::propagateNodePredictions):
+ (JSC::DFG::Propagator::fixupNode):
+ (JSC::DFG::Propagator::getIndexedPropertyStorageLoadElimination):
+ (JSC::DFG::Propagator::performNodeCSE):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileGetCharCodeAt):
+ (JSC::DFG::SpeculativeJIT::compileGetByValOnString):
+ (JSC::DFG::SpeculativeJIT::compileGetByValOnByteArray):
+ (JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray):
+ (JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray):
+ (JSC::DFG::SpeculativeJIT::compileGetIndexedPropertyStorage):
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+
2011-12-08 Fady Samuel <fsamuel@chromium.org>
[Chromium] Enable viewport metatag
diff --git a/Source/JavaScriptCore/bytecode/PredictedType.h b/Source/JavaScriptCore/bytecode/PredictedType.h
index 5720398..f41f336 100644
--- a/Source/JavaScriptCore/bytecode/PredictedType.h
+++ b/Source/JavaScriptCore/bytecode/PredictedType.h
@@ -60,6 +60,7 @@
static const PredictedType PredictBoolean = 0x00020000; // It's definitely a Boolean.
static const PredictedType PredictOther = 0x40000000; // It's definitely none of the above.
static const PredictedType PredictTop = 0x7fffffff; // It can be any of the above.
+static const PredictedType FixedIndexedStorageMask = PredictByteArray | PredictInt8Array | PredictInt16Array | PredictInt32Array | PredictUint8Array | PredictUint16Array | PredictUint32Array | PredictFloat32Array | PredictFloat64Array;
typedef bool (*PredictionChecker)(PredictedType);
@@ -83,6 +84,11 @@
return !!(value & (PredictFinalObject | PredictOther)) && !(value & ~(PredictFinalObject | PredictOther));
}
+inline bool isFixedIndexedStorageObjectPrediction(PredictedType value)
+{
+ return (value & FixedIndexedStorageMask) == value;
+}
+
inline bool isStringPrediction(PredictedType value)
{
return value == PredictString;
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index 373a0e0..b06a27c 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -758,12 +758,71 @@
forNode(node.child1()).set(node.structureTransitionData().newStructure);
m_haveStructures = true;
break;
-
case GetPropertyStorage:
forNode(node.child1()).filter(PredictCell);
forNode(nodeIndex).clear(); // The result is not a JS value.
break;
-
+ case GetIndexedPropertyStorage: {
+ PredictedType basePrediction = m_graph[node.child2()].prediction();
+ if (!(basePrediction & PredictInt32) && basePrediction) {
+ forNode(nodeIndex).clear();
+ break;
+ }
+ if (m_graph[node.child1()].prediction() == PredictString) {
+ forNode(node.child1()).filter(PredictString);
+ forNode(nodeIndex).clear();
+ break;
+ }
+ if (m_graph[node.child1()].shouldSpeculateByteArray()) {
+ forNode(node.child1()).filter(PredictByteArray);
+ forNode(nodeIndex).clear();
+ break;
+ }
+
+ if (m_graph[node.child1()].shouldSpeculateInt8Array()) {
+ forNode(node.child1()).filter(PredictInt8Array);
+ forNode(nodeIndex).clear();
+ break;
+ }
+ if (m_graph[node.child1()].shouldSpeculateInt16Array()) {
+ forNode(node.child1()).filter(PredictInt16Array);
+ forNode(nodeIndex).clear();
+ break;
+ }
+ if (m_graph[node.child1()].shouldSpeculateInt32Array()) {
+ forNode(node.child1()).filter(PredictInt32Array);
+ forNode(nodeIndex).clear();
+ break;
+ }
+ if (m_graph[node.child1()].shouldSpeculateUint8Array()) {
+ forNode(node.child1()).filter(PredictUint8Array);
+ forNode(nodeIndex).clear();
+ break;
+ }
+ if (m_graph[node.child1()].shouldSpeculateUint16Array()) {
+ forNode(node.child1()).filter(PredictUint16Array);
+ forNode(nodeIndex).set(PredictOther);
+ break;
+ }
+ if (m_graph[node.child1()].shouldSpeculateUint32Array()) {
+ forNode(node.child1()).filter(PredictUint32Array);
+ forNode(nodeIndex).clear();
+ break;
+ }
+ if (m_graph[node.child1()].shouldSpeculateFloat32Array()) {
+ forNode(node.child1()).filter(PredictFloat32Array);
+ forNode(nodeIndex).clear();
+ break;
+ }
+ if (m_graph[node.child1()].shouldSpeculateFloat64Array()) {
+ forNode(node.child1()).filter(PredictFloat64Array);
+ forNode(nodeIndex).clear();
+ break;
+ }
+ forNode(node.child1()).filter(PredictArray);
+ forNode(nodeIndex).clear();
+ break;
+ }
case GetByOffset:
forNode(node.child1()).filter(PredictCell);
forNode(nodeIndex).makeTop();
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 47287fb3..e8d80e0 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -1207,8 +1207,11 @@
case CharCodeAtIntrinsic: {
if (firstArg + 1 != lastArg)
return false;
-
- NodeIndex charCode = addToGraph(StringCharCodeAt, get(firstArg), getToInt32(firstArg + 1));
+ if (!(m_graph[get(firstArg)].prediction() & PredictString))
+ return false;
+
+ NodeIndex storage = addToGraph(GetIndexedPropertyStorage, get(firstArg), getToInt32(firstArg + 1));
+ NodeIndex charCode = addToGraph(StringCharCodeAt, get(firstArg), getToInt32(firstArg + 1), storage);
if (usesResult)
set(resultOperand, charCode);
return true;
@@ -1217,8 +1220,11 @@
case CharAtIntrinsic: {
if (firstArg + 1 != lastArg)
return false;
-
- NodeIndex charCode = addToGraph(StringCharAt, get(firstArg), getToInt32(firstArg + 1));
+ if (!(m_graph[get(firstArg)].prediction() & PredictString))
+ return false;
+
+ NodeIndex storage = addToGraph(GetIndexedPropertyStorage, get(firstArg), getToInt32(firstArg + 1));
+ NodeIndex charCode = addToGraph(StringCharAt, get(firstArg), getToInt32(firstArg + 1), storage);
if (usesResult)
set(resultOperand, charCode);
return true;
@@ -1611,8 +1617,8 @@
NodeIndex base = get(currentInstruction[2].u.operand);
NodeIndex property = get(currentInstruction[3].u.operand);
-
- NodeIndex getByVal = addToGraph(GetByVal, OpInfo(0), OpInfo(prediction), base, property);
+ NodeIndex propertyStorage = addToGraph(GetIndexedPropertyStorage, base, property);
+ NodeIndex getByVal = addToGraph(GetByVal, OpInfo(0), OpInfo(prediction), base, property, propertyStorage);
set(currentInstruction[1].u.operand, getByVal);
NEXT_OPCODE(op_get_by_val);
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index a827983..1045bdc 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -233,6 +233,7 @@
macro(CheckStructure, NodeMustGenerate) \
macro(PutStructure, NodeMustGenerate | NodeClobbersWorld) \
macro(GetPropertyStorage, NodeResultStorage) \
+ macro(GetIndexedPropertyStorage, NodeMustGenerate | NodeResultStorage) \
macro(GetByOffset, NodeResultJS) \
macro(PutByOffset, NodeMustGenerate | NodeClobbersWorld) \
macro(GetArrayLength, NodeResultInt32) \
diff --git a/Source/JavaScriptCore/dfg/DFGPropagator.cpp b/Source/JavaScriptCore/dfg/DFGPropagator.cpp
index 87b2715..764abe0 100644
--- a/Source/JavaScriptCore/dfg/DFGPropagator.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPropagator.cpp
@@ -472,11 +472,12 @@
break;
}
- case GetPropertyStorage: {
+ case GetPropertyStorage:
+ case GetIndexedPropertyStorage: {
changed |= setPrediction(PredictOther);
break;
}
-
+
case GetByOffset: {
if (node.getHeapPrediction())
changed |= mergePrediction(node.getHeapPrediction());
@@ -809,7 +810,23 @@
ASSERT_NOT_REACHED();
break;
}
-
+ case GetIndexedPropertyStorage: {
+ PredictedType basePrediction = m_graph[node.child2()].prediction();
+ if (!(basePrediction & PredictInt32) && basePrediction) {
+ node.op = Phantom;
+ node.children.fixed.child1 = NoNode;
+ node.children.fixed.child2 = NoNode;
+ node.children.fixed.child3 = NoNode;
+ }
+ break;
+ }
+ case GetByVal:
+ case StringCharAt:
+ case StringCharCodeAt: {
+ if (node.child3() != NoNode && m_graph[node.child3()].op == Phantom)
+ node.children.fixed.child3 = NoNode;
+ break;
+ }
default:
break;
}
@@ -1214,6 +1231,41 @@
}
return NoNode;
}
+
+ NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, bool hasIntegerIndexPrediction)
+ {
+ NodeIndex start = startIndexForChildren(child1);
+ for (NodeIndex index = m_compileIndex; index-- > start;) {
+ Node& node = m_graph[index];
+ switch (node.op) {
+ case GetIndexedPropertyStorage: {
+ PredictedType basePrediction = m_graph[node.child2()].prediction();
+ bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
+ if (node.child1() == child1 && hasIntegerIndexPrediction == nodeHasIntegerIndexPrediction)
+ return index;
+ break;
+ }
+
+ case PutByOffset:
+ case PutStructure:
+ // Changing the structure or putting to the storage cannot
+ // change the property storage pointer.
+ break;
+
+ case PutByVal:
+ case PutByValAlias:
+ if (isFixedIndexedStorageObjectPrediction(m_graph[node.child1()].prediction()) && byValIsPure(node))
+ break;
+ return NoNode;
+
+ default:
+ if (clobbersWorld(index))
+ return NoNode;
+ break;
+ }
+ }
+ return NoNode;
+ }
NodeIndex getScopeChainLoadElimination(unsigned depth)
{
@@ -1403,11 +1455,18 @@
if (checkFunctionElimination(node.function(), node.child1()))
eliminate();
break;
+
+ case GetIndexedPropertyStorage: {
+ PredictedType basePrediction = m_graph[node.child2()].prediction();
+ bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
+ setReplacement(getIndexedPropertyStorageLoadElimination(node.child1(), nodeHasIntegerIndexPrediction));
+ break;
+ }
case GetPropertyStorage:
setReplacement(getPropertyStorageLoadElimination(node.child1()));
break;
-
+
case GetByOffset:
setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber, node.child1()));
break;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index f8648c0d..51a64ca 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -1345,12 +1345,18 @@
ASSERT(node.child3() == NoNode);
SpeculateCellOperand string(this, node.child1());
SpeculateStrictInt32Operand index(this, node.child2());
+ StorageOperand storage(this, node.child3());
GPRReg stringReg = string.gpr();
GPRReg indexReg = index.gpr();
-
- if (!isStringPrediction(m_state.forNode(node.child1()).m_type))
- speculationCheck(JSValueSource::unboxedCell(stringReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(stringReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsStringVPtr)));
+ GPRReg storageReg = storage.gpr();
+
+ if (!isStringPrediction(m_state.forNode(node.child1()).m_type)) {
+ ASSERT(!(at(node.child1()).prediction() & PredictString));
+ terminateSpeculativeExecution(JSValueRegs(), NoNode);
+ noResult(m_compileIndex);
+ return;
+ }
// unsigned comparison so we can filter out negative indices and indices that are too large
speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, indexReg, MacroAssembler::Address(stringReg, JSString::offsetOfLength())));
@@ -1360,20 +1366,15 @@
m_jit.loadPtr(MacroAssembler::Address(stringReg, JSString::offsetOfValue()), scratchReg);
- // Speculate that we're not accessing a rope
- speculationCheck(JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, scratchReg));
-
// Load the character into scratchReg
JITCompiler::Jump is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(scratchReg, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit()));
- m_jit.loadPtr(MacroAssembler::Address(scratchReg, StringImpl::dataOffset()), scratchReg);
- m_jit.load8(MacroAssembler::BaseIndex(scratchReg, indexReg, MacroAssembler::TimesOne, 0), scratchReg);
+ m_jit.load8(MacroAssembler::BaseIndex(storageReg, indexReg, MacroAssembler::TimesOne, 0), scratchReg);
JITCompiler::Jump cont8Bit = m_jit.jump();
is16Bit.link(&m_jit);
- m_jit.loadPtr(MacroAssembler::Address(scratchReg, StringImpl::dataOffset()), scratchReg);
- m_jit.load16(MacroAssembler::BaseIndex(scratchReg, indexReg, MacroAssembler::TimesTwo, 0), scratchReg);
+ m_jit.load16(MacroAssembler::BaseIndex(storageReg, indexReg, MacroAssembler::TimesTwo, 0), scratchReg);
cont8Bit.link(&m_jit);
@@ -1382,15 +1383,19 @@
void SpeculativeJIT::compileGetByValOnString(Node& node)
{
- ASSERT(node.child3() == NoNode);
SpeculateCellOperand base(this, node.child1());
SpeculateStrictInt32Operand property(this, node.child2());
-
+ StorageOperand storage(this, node.child3());
GPRReg baseReg = base.gpr();
GPRReg propertyReg = property.gpr();
+ GPRReg storageReg = storage.gpr();
- if (!isStringPrediction(m_state.forNode(node.child1()).m_type))
- speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsStringVPtr)));
+ if (!isStringPrediction(m_state.forNode(node.child1()).m_type)) {
+ ASSERT(!(at(node.child1()).prediction() & PredictString));
+ terminateSpeculativeExecution(JSValueRegs(), NoNode);
+ noResult(m_compileIndex);
+ return;
+ }
// unsigned comparison so we can filter out negative indices and indices that are too large
speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSString::offsetOfLength())));
@@ -1400,20 +1405,15 @@
m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), scratchReg);
- // Speculate that we're not accessing a rope
- speculationCheck(JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, scratchReg));
-
// Load the character into scratchReg
JITCompiler::Jump is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(scratchReg, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit()));
- m_jit.loadPtr(MacroAssembler::Address(scratchReg, StringImpl::dataOffset()), scratchReg);
- m_jit.load8(MacroAssembler::BaseIndex(scratchReg, propertyReg, MacroAssembler::TimesOne, 0), scratchReg);
+ m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne, 0), scratchReg);
JITCompiler::Jump cont8Bit = m_jit.jump();
is16Bit.link(&m_jit);
- m_jit.loadPtr(MacroAssembler::Address(scratchReg, StringImpl::dataOffset()), scratchReg);
- m_jit.load16(MacroAssembler::BaseIndex(scratchReg, propertyReg, MacroAssembler::TimesTwo, 0), scratchReg);
+ m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo, 0), scratchReg);
// We only support ascii characters
speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, scratchReg, TrustedImm32(0x100)));
@@ -1555,15 +1555,17 @@
void SpeculativeJIT::compileGetByValOnByteArray(Node& node)
{
- ASSERT(node.child3() == NoNode);
SpeculateCellOperand base(this, node.child1());
SpeculateStrictInt32Operand property(this, node.child2());
-
+
GPRReg baseReg = base.gpr();
GPRReg propertyReg = property.gpr();
-
- if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type))
- speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsByteArrayVPtr)));
+
+ if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type)) {
+ terminateSpeculativeExecution(JSValueRegs(), NoNode);
+ noResult(m_compileIndex);
+ return;
+ }
// Load the character into scratchReg
GPRTemporary storage(this);
@@ -1595,48 +1597,51 @@
void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& descriptor, Node& node, size_t elementSize, TypedArraySpeculationRequirements speculationRequirements, TypedArraySignedness signedness)
{
- ASSERT(node.child3() == NoNode);
SpeculateCellOperand base(this, node.child1());
SpeculateStrictInt32Operand property(this, node.child2());
+ StorageOperand storage(this, node.child3());
+
GPRReg baseReg = base.gpr();
GPRReg propertyReg = property.gpr();
-
- if (speculationRequirements != NoTypedArrayTypeSpecCheck)
- speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
-
- // Load the character into scratchReg
- GPRTemporary storage(this);
GPRReg storageReg = storage.gpr();
- m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
-
- ASSERT(speculationRequirements != NoTypedArraySpecCheck);
+
+ GPRTemporary result(this);
+ GPRReg resultReg = result.gpr();
+
+ if (speculationRequirements != NoTypedArrayTypeSpecCheck) {
+ ASSERT_NOT_REACHED();
+ terminateSpeculativeExecution(JSValueRegs(), NoNode);
+ noResult(m_compileIndex);
+ return;
+ }
+
MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(baseReg, descriptor.m_lengthOffset));
- m_jit.xorPtr(storageReg, storageReg);
+ m_jit.xorPtr(resultReg, resultReg);
MacroAssembler::Jump outOfBounds = m_jit.jump();
inBounds.link(&m_jit);
switch (elementSize) {
case 1:
- m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne), storageReg);
+ m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne), resultReg);
break;
case 2:
- m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), storageReg);
+ m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), resultReg);
break;
case 4:
- m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), storageReg);
+ m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), resultReg);
break;
default:
ASSERT_NOT_REACHED();
}
outOfBounds.link(&m_jit);
if (elementSize < 4 || signedness == SignedTypedArray)
- integerResult(storageReg, m_compileIndex);
+ integerResult(resultReg, m_compileIndex);
else {
- FPRTemporary result(this);
- m_jit.convertInt32ToDouble(storageReg, result.fpr());
- JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, storageReg, TrustedImm32(0));
- m_jit.addDouble(JITCompiler::AbsoluteAddress(&twoToThe32), result.fpr());
+ FPRTemporary fresult(this);
+ m_jit.convertInt32ToDouble(resultReg, fresult.fpr());
+ JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, resultReg, TrustedImm32(0));
+ m_jit.addDouble(JITCompiler::AbsoluteAddress(&twoToThe32), fresult.fpr());
positive.link(&m_jit);
- doubleResult(result.fpr(), m_compileIndex);
+ doubleResult(fresult.fpr(), m_compileIndex);
}
}
@@ -1718,26 +1723,27 @@
void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor& descriptor, Node& node, size_t elementSize, TypedArraySpeculationRequirements speculationRequirements)
{
- ASSERT(node.child3() == NoNode);
SpeculateCellOperand base(this, node.child1());
SpeculateStrictInt32Operand property(this, node.child2());
+ StorageOperand storage(this, node.child3());
+
GPRReg baseReg = base.gpr();
GPRReg propertyReg = property.gpr();
-
- if (speculationRequirements != NoTypedArrayTypeSpecCheck)
- speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
-
- // Load the character into scratchReg
- GPRTemporary storage(this);
GPRReg storageReg = storage.gpr();
- m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+
+ if (speculationRequirements != NoTypedArrayTypeSpecCheck) {
+ ASSERT_NOT_REACHED();
+ terminateSpeculativeExecution(JSValueRegs(), NoNode);
+ noResult(m_compileIndex);
+ return;
+ }
+
FPRTemporary result(this);
FPRReg resultReg = result.fpr();
ASSERT(speculationRequirements != NoTypedArraySpecCheck);
- GPRTemporary scratch(this);
MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(baseReg, descriptor.m_lengthOffset));
- m_jit.move(MacroAssembler::Imm32(0), scratch.gpr());
- m_jit.convertInt32ToDouble(scratch.gpr(), resultReg);
+ static const double zero = 0;
+ m_jit.loadDouble(&zero, resultReg);
MacroAssembler::Jump outOfBounds = m_jit.jump();
inBounds.link(&m_jit);
switch (elementSize) {
@@ -2364,6 +2370,83 @@
return nonSpeculativeStrictEq(node);
}
+void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node)
+{
+ SpeculateCellOperand base(this, node.child1());
+ GPRReg baseReg = base.gpr();
+
+ PredictedType basePrediction = at(node.child2()).prediction();
+ if (!(basePrediction & PredictInt32) && basePrediction) {
+ ASSERT_NOT_REACHED();
+ terminateSpeculativeExecution(JSValueRegs(), NoNode);
+ noResult(m_compileIndex);
+ return;
+ }
+
+ GPRTemporary storage(this);
+ GPRReg storageReg = storage.gpr();
+ if (at(node.child1()).prediction() == PredictString) {
+ if (!isStringPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsStringVPtr)));
+
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), storageReg);
+
+ // Speculate that we're not accessing a rope
+ speculationCheck(JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, storageReg));
+
+ m_jit.loadPtr(MacroAssembler::Address(storageReg, StringImpl::dataOffset()), storageReg);
+ } else if (at(node.child1()).shouldSpeculateByteArray()) {
+ if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsByteArrayVPtr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, JSByteArray::offsetOfStorage()), storageReg);
+ } else if (at(node.child1()).shouldSpeculateInt8Array()) {
+ const TypedArrayDescriptor& descriptor = m_jit.globalData()->int8ArrayDescriptor();
+ if (!isInt8ArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+ } else if (at(node.child1()).shouldSpeculateInt16Array()) {
+ const TypedArrayDescriptor& descriptor = m_jit.globalData()->int16ArrayDescriptor();
+ if (!isInt16ArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+ } else if (at(node.child1()).shouldSpeculateInt32Array()) {
+ const TypedArrayDescriptor& descriptor = m_jit.globalData()->int32ArrayDescriptor();
+ if (!isInt32ArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+ } else if (at(node.child1()).shouldSpeculateUint8Array()) {
+ const TypedArrayDescriptor& descriptor = m_jit.globalData()->uint8ArrayDescriptor();
+ if (!isUint8ArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+ } else if (at(node.child1()).shouldSpeculateUint16Array()) {
+ const TypedArrayDescriptor& descriptor = m_jit.globalData()->uint16ArrayDescriptor();
+ if (!isUint16ArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+ } else if (at(node.child1()).shouldSpeculateUint32Array()) {
+ const TypedArrayDescriptor& descriptor = m_jit.globalData()->uint32ArrayDescriptor();
+ if (!isUint32ArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+ } else if (at(node.child1()).shouldSpeculateFloat32Array()) {
+ const TypedArrayDescriptor& descriptor = m_jit.globalData()->float32ArrayDescriptor();
+ if (!isFloat32ArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+ } else if (at(node.child1()).shouldSpeculateFloat64Array()) {
+ const TypedArrayDescriptor& descriptor = m_jit.globalData()->float64ArrayDescriptor();
+ if (!isFloat64ArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+ } else {
+ if (!isArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
+ }
+ storageResult(storageReg, m_compileIndex);
+}
+
} } // namespace JSC::DFG
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 2d109ba..7b03fbe 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -1967,6 +1967,7 @@
SignedTypedArray,
UnsignedTypedArray
};
+ void compileGetIndexedPropertyStorage(Node&);
void compileGetByValOnIntTypedArray(const TypedArrayDescriptor&, Node&, size_t elementSize, TypedArraySpeculationRequirements, TypedArraySignedness);
void compilePutByValForIntTypedArray(const TypedArrayDescriptor&, GPRReg base, GPRReg property, Node&, size_t elementSize, TypedArraySpeculationRequirements, TypedArraySignedness);
void compileGetByValOnFloatTypedArray(const TypedArrayDescriptor&, Node&, size_t elementSize, TypedArraySpeculationRequirements);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 74b4126..654b3f64f 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -2684,37 +2684,35 @@
break;
}
- ASSERT(node.child3() == NoNode);
- SpeculateCellOperand base(this, node.child1());
SpeculateStrictInt32Operand property(this, node.child2());
- GPRTemporary storage(this);
+ StorageOperand storage(this, node.child3());
GPRTemporary resultTag(this);
-
- GPRReg baseReg = base.gpr();
+ GPRTemporary resultPayload(this);
+
GPRReg propertyReg = property.gpr();
GPRReg storageReg = storage.gpr();
if (!m_compileOkay)
return;
- // Get the array storage. We haven't yet checked this is a JSArray, so this is only safe if
- // an access with offset JSArray::storageOffset() is valid for all JSCells!
- m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
-
// Check that base is an array, and that property is contained within m_vector (< m_vectorLength).
// If we have predicted the base to be type array, we can skip the check.
- if (!isArrayPrediction(m_state.forNode(node.child1()).m_type))
- speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
- speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
+ {
+ SpeculateCellOperand base(this, node.child1());
+ GPRReg baseReg = base.gpr();
+ if (!isArrayPrediction(m_state.forNode(node.child1()).m_type))
+ speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
+ speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
+ }
// FIXME: In cases where there are subsequent by_val accesses to the same base it might help to cache
// the storage pointer - especially if there happens to be another register free right now. If we do so,
// then we'll need to allocate a new temporary for result.
m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr());
speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag)));
- m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), storageReg);
+ m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr());
- jsValueResult(resultTag.gpr(), storageReg, m_compileIndex);
+ jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex);
break;
}
@@ -3658,7 +3656,12 @@
storageResult(resultGPR, m_compileIndex);
break;
}
-
+
+ case GetIndexedPropertyStorage: {
+ compileGetIndexedPropertyStorage(node);
+ break;
+ }
+
case GetByOffset: {
StorageOperand storage(this, node.child1());
GPRTemporary resultTag(this, storage);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 532700c..1b5810b 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -2689,10 +2689,9 @@
break;
}
- ASSERT(node.child3() == NoNode);
SpeculateCellOperand base(this, node.child1());
SpeculateStrictInt32Operand property(this, node.child2());
- GPRTemporary storage(this);
+ StorageOperand storage(this, node.child3());
GPRReg baseReg = base.gpr();
GPRReg propertyReg = property.gpr();
@@ -2701,12 +2700,6 @@
if (!m_compileOkay)
return;
- // Get the array storage. We haven't yet checked this is a JSArray, so this is only safe if
- // an access with offset JSArray::storageOffset() is valid for all JSCells!
- m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
-
- // Check that base is an array, and that property is contained within m_vector (< m_vectorLength).
- // If we have predicted the base to be type array, we can skip the check.
if (!isArrayPrediction(m_state.forNode(node.child1()).m_type))
speculationCheck(JSValueRegs(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
@@ -2714,7 +2707,7 @@
// FIXME: In cases where there are subsequent by_val accesses to the same base it might help to cache
// the storage pointer - especially if there happens to be another register free right now. If we do so,
// then we'll need to allocate a new temporary for result.
- GPRTemporary& result = storage;
+ GPRTemporary result(this);
m_jit.loadPtr(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), result.gpr());
speculationCheck(JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()));
@@ -2864,7 +2857,7 @@
case PutByValAlias: {
PredictedType basePrediction = at(node.child2()).prediction();
ASSERT_UNUSED(basePrediction, (basePrediction & PredictInt32) || !basePrediction);
-
+
SpeculateCellOperand base(this, node.child1());
SpeculateStrictInt32Operand property(this, node.child2());
if (at(node.child1()).shouldSpeculateByteArray()) {
@@ -3610,6 +3603,11 @@
storageResult(resultGPR, m_compileIndex);
break;
}
+
+ case GetIndexedPropertyStorage: {
+ compileGetIndexedPropertyStorage(node);
+ break;
+ }
case GetByOffset: {
StorageOperand storage(this, node.child1());