DFG should trigger rage conversion from double to contiguous if it sees a GetByVal on Double being used in an integer context
https://bugs.webkit.org/show_bug.cgi?id=103858
Reviewed by Gavin Barraclough.
A rage conversion from double to contiguous is one where you try to convert each
double to an int32.
This is probably not the last we'll hear of rage conversion from double to contiguous.
It may be better to do this right during parsing, which will result in fewer cases of
Arrayification. But even so, this looks like a straight win already - 1% speed-up on
Kraken, no major regression anywhere else.
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::ArrayMode::refine):
(JSC::DFG::arrayConversionToString):
(JSC::DFG::ArrayMode::dump):
(WTF):
(WTF::printInternal):
* dfg/DFGArrayMode.h:
(JSC::DFG::ArrayMode::withConversion):
(ArrayMode):
(JSC::DFG::ArrayMode::doesConversion):
(WTF):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupBlock):
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::checkArray):
(FixupPhase):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGNodeFlags.h:
(DFG):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::arrayify):
* dfg/DFGStructureCheckHoistingPhase.cpp:
(JSC::DFG::StructureCheckHoistingPhase::run):
* runtime/JSObject.cpp:
(JSC):
(JSC::JSObject::genericConvertDoubleToContiguous):
(JSC::JSObject::convertDoubleToContiguous):
(JSC::JSObject::rageConvertDoubleToContiguous):
(JSC::JSObject::ensureContiguousSlow):
(JSC::JSObject::rageEnsureContiguousSlow):
* runtime/JSObject.h:
(JSObject):
(JSC::JSObject::rageEnsureContiguous):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@136372 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 74a44da..8489c9ba 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,5 +1,61 @@
2012-12-02 Filip Pizlo <fpizlo@apple.com>
+ DFG should trigger rage conversion from double to contiguous if it sees a GetByVal on Double being used in an integer context
+ https://bugs.webkit.org/show_bug.cgi?id=103858
+
+ Reviewed by Gavin Barraclough.
+
+ A rage conversion from double to contiguous is one where you try to convert each
+ double to an int32.
+
+ This is probably not the last we'll hear of rage conversion from double to contiguous.
+ It may be better to do this right during parsing, which will result in fewer cases of
+ Arrayification. But even so, this looks like a straight win already - 1% speed-up on
+ Kraken, no major regression anywhere else.
+
+ * dfg/DFGAbstractState.cpp:
+ (JSC::DFG::AbstractState::execute):
+ * dfg/DFGArrayMode.cpp:
+ (JSC::DFG::ArrayMode::refine):
+ (JSC::DFG::arrayConversionToString):
+ (JSC::DFG::ArrayMode::dump):
+ (WTF):
+ (WTF::printInternal):
+ * dfg/DFGArrayMode.h:
+ (JSC::DFG::ArrayMode::withConversion):
+ (ArrayMode):
+ (JSC::DFG::ArrayMode::doesConversion):
+ (WTF):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupBlock):
+ (JSC::DFG::FixupPhase::fixupNode):
+ (JSC::DFG::FixupPhase::checkArray):
+ (FixupPhase):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ * dfg/DFGNodeFlags.h:
+ (DFG):
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::arrayify):
+ * dfg/DFGStructureCheckHoistingPhase.cpp:
+ (JSC::DFG::StructureCheckHoistingPhase::run):
+ * runtime/JSObject.cpp:
+ (JSC):
+ (JSC::JSObject::genericConvertDoubleToContiguous):
+ (JSC::JSObject::convertDoubleToContiguous):
+ (JSC::JSObject::rageConvertDoubleToContiguous):
+ (JSC::JSObject::ensureContiguousSlow):
+ (JSC::JSObject::rageEnsureContiguousSlow):
+ * runtime/JSObject.h:
+ (JSObject):
+ (JSC::JSObject::rageEnsureContiguous):
+
+2012-12-02 Filip Pizlo <fpizlo@apple.com>
+
DFG CSE should not keep alive things that aren't relevant to OSR
https://bugs.webkit.org/show_bug.cgi?id=103849
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index 64e5f3f..a1dc064 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -1562,7 +1562,8 @@
node.setCanExit(false);
break;
}
- ASSERT(node.arrayMode().conversion() == Array::Convert);
+ ASSERT(node.arrayMode().conversion() == Array::Convert
+ || node.arrayMode().conversion() == Array::RageConvert);
node.setCanExit(true);
forNode(node.child1()).filter(SpecCell);
if (node.child2())
diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp
index 3bfb6a4..e730482 100644
--- a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp
+++ b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp
@@ -122,7 +122,7 @@
}
}
-ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index, SpeculatedType value) const
+ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index, SpeculatedType value, NodeFlags flags) const
{
if (!base || !index) {
// It can be that we had a legitimate arrayMode but no incoming predictions. That'll
@@ -156,10 +156,17 @@
return withTypeAndConversion(Array::Contiguous, Array::Convert);
case Array::Double:
+ if (flags & NodeUsedAsIntLocally)
+ return withTypeAndConversion(Array::Contiguous, Array::RageConvert);
if (!value || isNumberSpeculation(value))
return *this;
return withTypeAndConversion(Array::Contiguous, Array::Convert);
+ case Array::Contiguous:
+ if (doesConversion() && (flags & NodeUsedAsIntLocally))
+ return withConversion(Array::RageConvert);
+ return *this;
+
case Array::SelectUsingPredictions:
if (isStringSpeculation(base))
return ArrayMode(Array::String);
@@ -429,19 +436,43 @@
return "AsIs";
case Array::Convert:
return "Convert";
+ case Array::RageConvert:
+ return "RageConvert";
default:
return "Unknown!";
}
}
-const char* ArrayMode::toString() const
+void ArrayMode::dump(PrintStream& out) const
{
- static char buffer[256];
- snprintf(buffer, sizeof(buffer), "%s%s%s%s", arrayTypeToString(type()), arrayClassToString(arrayClass()), arraySpeculationToString(speculation()), arrayConversionToString(conversion()));
- return buffer;
+ out.print(type(), arrayClass(), speculation(), conversion());
}
} } // namespace JSC::DFG
+namespace WTF {
+
+void printInternal(PrintStream& out, JSC::DFG::Array::Type type)
+{
+ out.print(JSC::DFG::arrayTypeToString(type));
+}
+
+void printInternal(PrintStream& out, JSC::DFG::Array::Class arrayClass)
+{
+ out.print(JSC::DFG::arrayClassToString(arrayClass));
+}
+
+void printInternal(PrintStream& out, JSC::DFG::Array::Speculation speculation)
+{
+ out.print(JSC::DFG::arraySpeculationToString(speculation));
+}
+
+void printInternal(PrintStream& out, JSC::DFG::Array::Conversion conversion)
+{
+ out.print(JSC::DFG::arrayConversionToString(conversion));
+}
+
+} // namespace WTF
+
#endif // ENABLE(DFG_JIT)
diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.h b/Source/JavaScriptCore/dfg/DFGArrayMode.h
index 0799868..3b71183 100644
--- a/Source/JavaScriptCore/dfg/DFGArrayMode.h
+++ b/Source/JavaScriptCore/dfg/DFGArrayMode.h
@@ -31,6 +31,7 @@
#if ENABLE(DFG_JIT)
#include "ArrayProfile.h"
+#include "DFGNodeFlags.h"
#include "SpeculatedType.h"
namespace JSC {
@@ -93,7 +94,8 @@
};
enum Conversion {
AsIs,
- Convert
+ Convert,
+ RageConvert
};
} // namespace Array
@@ -183,16 +185,21 @@
return ArrayMode(type, arrayClass(), speculation(), conversion());
}
+ ArrayMode withConversion(Array::Conversion conversion) const
+ {
+ return ArrayMode(type(), arrayClass(), speculation(), conversion);
+ }
+
ArrayMode withTypeAndConversion(Array::Type type, Array::Conversion conversion) const
{
return ArrayMode(type, arrayClass(), speculation(), conversion);
}
- ArrayMode refine(SpeculatedType base, SpeculatedType index, SpeculatedType value = SpecNone) const;
+ ArrayMode refine(SpeculatedType base, SpeculatedType index, SpeculatedType value = SpecNone, NodeFlags = 0) const;
bool alreadyChecked(Graph&, Node&, AbstractValue&) const;
- const char* toString() const;
+ void dump(PrintStream&) const;
bool usesButterfly() const
{
@@ -353,7 +360,7 @@
bool doesConversion() const
{
- return conversion() == Array::Convert;
+ return conversion() != Array::AsIs;
}
ArrayModes arrayModesThatPassFiltering() const
@@ -435,6 +442,16 @@
} } // namespace JSC::DFG
+namespace WTF {
+
+class PrintStream;
+void printInternal(PrintStream&, JSC::DFG::Array::Type);
+void printInternal(PrintStream&, JSC::DFG::Array::Class);
+void printInternal(PrintStream&, JSC::DFG::Array::Speculation);
+void printInternal(PrintStream&, JSC::DFG::Array::Conversion);
+
+} // namespace WTF
+
#endif // ENABLE(DFG_JIT)
#endif // DFGArrayMode_h
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 1ba40de..64a778e 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -54,6 +54,7 @@
if (!block)
return;
ASSERT(block->isReachable);
+ m_block = block;
for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) {
m_compileIndex = block->at(m_indexInBlock);
fixupNode(m_graph[m_compileIndex]);
@@ -131,7 +132,8 @@
node.setArrayMode(
node.arrayMode().refine(
m_graph[node.child1()].prediction(),
- m_graph[node.child2()].prediction()));
+ m_graph[node.child2()].prediction(),
+ SpecNone, node.flags()));
blessArrayOperation(node.child1(), node.child2(), 2);
@@ -442,6 +444,16 @@
m_graph.ref(index);
if (structure) {
+ if (m_indexInBlock > 0) {
+ // If the previous node was a CheckStructure inserted because of stuff
+ // that the array profile told us, then remove it.
+ Node& previousNode = m_graph[m_block->at(m_indexInBlock - 1)];
+ if (previousNode.op() == CheckStructure
+ && previousNode.child1() == array
+ && previousNode.codeOrigin == codeOrigin)
+ previousNode.setOpAndDefaultFlags(Phantom);
+ }
+
Node arrayify(ArrayifyToStructure, codeOrigin, OpInfo(structure), OpInfo(arrayMode.asWord()), array, index);
arrayify.ref();
NodeIndex arrayifyIndex = m_graph.size();
@@ -564,7 +576,8 @@
int32ToDouble.predict(SpecDouble);
int32ToDouble.ref();
}
-
+
+ BasicBlock* m_block;
unsigned m_indexInBlock;
NodeIndex m_compileIndex;
InsertionSet<NodeIndex> m_insertionSet;
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp
index 9fcf61a..c19ce2c 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp
+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp
@@ -189,7 +189,7 @@
hasPrinted = true;
}
if (node.hasArrayMode()) {
- out.print(hasPrinted ? ", " : "", node.arrayMode().toString());
+ out.print(hasPrinted ? ", " : "", node.arrayMode());
hasPrinted = true;
}
if (node.hasVarNumber()) {
diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.h b/Source/JavaScriptCore/dfg/DFGNodeFlags.h
index 5e41bfc..18127c5 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeFlags.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.h
@@ -52,15 +52,16 @@
#define NodeMayOverflow 0x100
#define NodeMayNegZero 0x200
-#define NodeBackPropMask 0x3C00
-#define NodeUseBottom 0x000
+#define NodeBackPropMask 0x7C00
+#define NodeUseBottom 0x0000
#define NodeUsedAsNumber 0x400 // The result of this computation may be used in a context that observes fractional results.
#define NodeNeedsNegZero 0x800 // The result of this computation may be used in a context that observes -0.
#define NodeUsedAsOther 0x1000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined).
#define NodeUsedAsValue (NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther)
#define NodeUsedAsInt 0x2000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
+#define NodeUsedAsIntLocally 0x4000 // Same as NodeUsedAsInt, but within the same basic block.
-#define NodeDoesNotExit 0x4000 // This flag is negated to make it natural for the default to be that a node does exit.
+#define NodeDoesNotExit 0x8000 // This flag is negated to make it natural for the default to be that a node does exit.
typedef uint16_t NodeFlags;
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index ad595ae..bc44d25 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -1444,6 +1444,14 @@
return reinterpret_cast<char*>(object->ensureContiguous(globalData));
}
+char* DFG_OPERATION operationRageEnsureContiguous(ExecState* exec, JSObject* object)
+{
+ JSGlobalData& globalData = exec->globalData();
+ NativeCallFrameTracer tracer(&globalData, exec);
+
+ return reinterpret_cast<char*>(object->rageEnsureContiguous(globalData));
+}
+
char* DFG_OPERATION operationEnsureArrayStorage(ExecState* exec, JSObject* object)
{
JSGlobalData& globalData = exec->globalData();
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h
index 00e6b07..6797bad 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.h
+++ b/Source/JavaScriptCore/dfg/DFGOperations.h
@@ -205,6 +205,7 @@
char* DFG_OPERATION operationEnsureInt32(ExecState*, JSObject*);
char* DFG_OPERATION operationEnsureDouble(ExecState*, JSObject*);
char* DFG_OPERATION operationEnsureContiguous(ExecState*, JSObject*);
+char* DFG_OPERATION operationRageEnsureContiguous(ExecState*, JSObject*);
char* DFG_OPERATION operationEnsureArrayStorage(ExecState*, JSObject*);
// This method is used to lookup an exception hander, keyed by faultLocation, which is
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index c149a1a..47cfbd6 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -204,7 +204,7 @@
if (prediction)
changed |= mergePrediction(prediction);
- changed |= variableAccessData->mergeFlags(flags);
+ changed |= variableAccessData->mergeFlags(flags & ~NodeUsedAsIntLocally);
break;
}
@@ -229,7 +229,7 @@
case BitLShift:
case BitURShift: {
changed |= setPrediction(SpecInt32);
- flags |= NodeUsedAsInt;
+ flags |= NodeUsedAsInt | NodeUsedAsIntLocally;
flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther);
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
@@ -238,7 +238,7 @@
case ValueToInt32: {
changed |= setPrediction(SpecInt32);
- flags |= NodeUsedAsInt;
+ flags |= NodeUsedAsInt | NodeUsedAsIntLocally;
flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther);
changed |= m_graph[node.child1()].mergeFlags(flags);
break;
@@ -267,7 +267,7 @@
case StringCharCodeAt: {
changed |= mergePrediction(SpecInt32);
changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
+ changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt | NodeUsedAsIntLocally);
break;
}
@@ -517,13 +517,13 @@
changed |= mergePrediction(node.getHeapPrediction());
changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
+ changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt | NodeUsedAsIntLocally);
break;
}
case GetMyArgumentByValSafe: {
changed |= mergePrediction(node.getHeapPrediction());
- changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
+ changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt | NodeUsedAsIntLocally);
break;
}
@@ -626,7 +626,7 @@
case NewArrayWithSize: {
changed |= setPrediction(SpecArray);
- changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue | NodeUsedAsInt);
+ changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue | NodeUsedAsInt | NodeUsedAsIntLocally);
break;
}
@@ -643,7 +643,7 @@
case StringCharAt: {
changed |= setPrediction(SpecString);
changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
+ changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt | NodeUsedAsIntLocally);
break;
}
@@ -718,7 +718,7 @@
case PutByVal:
changed |= m_graph[m_graph.varArgChild(node, 0)].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
+ changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt | NodeUsedAsIntLocally);
changed |= m_graph[m_graph.varArgChild(node, 2)].mergeFlags(NodeUsedAsValue);
break;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index d7f7b2f..a9663e6 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -598,7 +598,10 @@
callOperation(operationEnsureDouble, tempGPR, baseReg);
break;
case Array::Contiguous:
- callOperation(operationEnsureContiguous, tempGPR, baseReg);
+ if (node.arrayMode().conversion() == Array::RageConvert)
+ callOperation(operationRageEnsureContiguous, tempGPR, baseReg);
+ else
+ callOperation(operationEnsureContiguous, tempGPR, baseReg);
break;
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
index 4f238e6..130ed33 100644
--- a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
@@ -106,6 +106,24 @@
// Don't count these uses.
break;
+ case ArrayifyToStructure:
+ case Arrayify:
+ if (node.arrayMode().conversion() == Array::RageConvert) {
+ // Rage conversion changes structures. We should avoid tying to do
+ // any kind of hoisting when rage conversion is in play.
+ Node& child = m_graph[node.child1()];
+ if (child.op() != GetLocal)
+ break;
+ VariableAccessData* variable = child.variableAccessData();
+ variable->vote(VoteOther);
+ if (variable->isCaptured() || variable->structureCheckHoistingFailed())
+ break;
+ if (!isCellSpeculation(variable->prediction()))
+ break;
+ noticeStructureCheck(variable, 0);
+ }
+ break;
+
case SetLocal: {
// Find all uses of the source of the SetLocal. If any of them are a
// kind of CheckStructure, then we should notice them to ensure that
diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp
index dc73e04..96b732a 100644
--- a/Source/JavaScriptCore/runtime/JSObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSObject.cpp
@@ -804,7 +804,8 @@
return convertInt32ToArrayStorage(globalData, structure()->suggestedArrayStorageTransition());
}
-WriteBarrier<Unknown>* JSObject::convertDoubleToContiguous(JSGlobalData& globalData)
+template<JSObject::DoubleToContiguousMode mode>
+WriteBarrier<Unknown>* JSObject::genericConvertDoubleToContiguous(JSGlobalData& globalData)
{
ASSERT(hasDouble(structure()->indexingType()));
@@ -816,13 +817,33 @@
currentAsValue->clear();
continue;
}
- currentAsValue->setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
+ JSValue v;
+ switch (mode) {
+ case EncodeValueAsDouble:
+ v = JSValue(JSValue::EncodeAsDouble, value);
+ break;
+ case RageConvertDoubleToValue:
+ v = jsNumber(value);
+ break;
+ }
+ ASSERT(v.isNumber());
+ currentAsValue->setWithoutWriteBarrier(v);
}
setStructure(globalData, Structure::nonPropertyTransition(globalData, structure(), AllocateContiguous));
return m_butterfly->contiguous();
}
+WriteBarrier<Unknown>* JSObject::convertDoubleToContiguous(JSGlobalData& globalData)
+{
+ return genericConvertDoubleToContiguous<EncodeValueAsDouble>(globalData);
+}
+
+WriteBarrier<Unknown>* JSObject::rageConvertDoubleToContiguous(JSGlobalData& globalData)
+{
+ return genericConvertDoubleToContiguous<RageConvertDoubleToValue>(globalData);
+}
+
ArrayStorage* JSObject::convertDoubleToArrayStorage(JSGlobalData& globalData, NonPropertyTransition transition, unsigned neededLength)
{
ASSERT(hasDouble(structure()->indexingType()));
@@ -974,7 +995,7 @@
}
}
-WriteBarrier<Unknown>* JSObject::ensureContiguousSlow(JSGlobalData& globalData)
+WriteBarrier<Unknown>* JSObject::ensureContiguousSlow(JSGlobalData& globalData, DoubleToContiguousMode mode)
{
switch (structure()->indexingType()) {
case ALL_BLANK_INDEXING_TYPES:
@@ -989,6 +1010,8 @@
return convertInt32ToContiguous(globalData);
case ALL_DOUBLE_INDEXING_TYPES:
+ if (mode == RageConvertDoubleToValue)
+ return rageConvertDoubleToContiguous(globalData);
return convertDoubleToContiguous(globalData);
case ALL_ARRAY_STORAGE_INDEXING_TYPES:
@@ -1000,6 +1023,16 @@
}
}
+WriteBarrier<Unknown>* JSObject::ensureContiguousSlow(JSGlobalData& globalData)
+{
+ return ensureContiguousSlow(globalData, EncodeValueAsDouble);
+}
+
+WriteBarrier<Unknown>* JSObject::rageEnsureContiguousSlow(JSGlobalData& globalData)
+{
+ return ensureContiguousSlow(globalData, RageConvertDoubleToValue);
+}
+
ArrayStorage* JSObject::ensureArrayStorageSlow(JSGlobalData& globalData)
{
switch (structure()->indexingType()) {
diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h
index 4f7f470..e5ccd20 100644
--- a/Source/JavaScriptCore/runtime/JSObject.h
+++ b/Source/JavaScriptCore/runtime/JSObject.h
@@ -679,6 +679,17 @@
return ensureContiguousSlow(globalData);
}
+ // Same as ensureContiguous(), except that if the indexed storage is in
+ // double mode, then it does a rage conversion to contiguous: it
+ // attempts to convert each double to an int32.
+ WriteBarrier<Unknown>* rageEnsureContiguous(JSGlobalData& globalData)
+ {
+ if (LIKELY(hasContiguous(structure()->indexingType())))
+ return m_butterfly->contiguous();
+
+ return rageEnsureContiguousSlow(globalData);
+ }
+
// Ensure that the object is in a mode where it has array storage. Use
// this if you're about to perform actions that would have required the
// object to be converted to have array storage, if it didn't have it
@@ -775,8 +786,9 @@
ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength);
ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&, NonPropertyTransition);
ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&);
-
+
WriteBarrier<Unknown>* convertDoubleToContiguous(JSGlobalData&);
+ WriteBarrier<Unknown>* rageConvertDoubleToContiguous(JSGlobalData&);
ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength);
ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&, NonPropertyTransition);
ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&);
@@ -965,8 +977,14 @@
WriteBarrier<Unknown>* ensureInt32Slow(JSGlobalData&);
double* ensureDoubleSlow(JSGlobalData&);
WriteBarrier<Unknown>* ensureContiguousSlow(JSGlobalData&);
+ WriteBarrier<Unknown>* rageEnsureContiguousSlow(JSGlobalData&);
ArrayStorage* ensureArrayStorageSlow(JSGlobalData&);
-
+
+ enum DoubleToContiguousMode { EncodeValueAsDouble, RageConvertDoubleToValue };
+ template<DoubleToContiguousMode mode>
+ WriteBarrier<Unknown>* genericConvertDoubleToContiguous(JSGlobalData&);
+ WriteBarrier<Unknown>* ensureContiguousSlow(JSGlobalData&, DoubleToContiguousMode);
+
protected:
Butterfly* m_butterfly;
};