diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg')
32 files changed, 1177 insertions, 1061 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index 43b5a03f3..5f79f666f 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -108,24 +108,6 @@ void AbstractState::initialize(Graph& graph) root->valuesAtHead.argument(i).set(SpecInt32); else if (isBooleanSpeculation(prediction)) root->valuesAtHead.argument(i).set(SpecBoolean); - else if (isInt8ArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecInt8Array); - else if (isInt16ArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecInt16Array); - else if (isInt32ArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecInt32Array); - else if (isUint8ArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecUint8Array); - else if (isUint8ClampedArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecUint8ClampedArray); - else if (isUint16ArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecUint16Array); - else if (isUint32ArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecUint32Array); - else if (isFloat32ArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecFloat32Array); - else if (isFloat64ArraySpeculation(prediction)) - root->valuesAtHead.argument(i).set(SpecFloat64Array); else if (isCellSpeculation(prediction)) root->valuesAtHead.argument(i).set(SpecCell); else @@ -626,14 +608,9 @@ bool AbstractState::execute(unsigned indexInBlock) Node& child = m_graph[node.child1()]; if (isBooleanSpeculation(child.prediction())) speculateBooleanUnary(node); - else if (child.shouldSpeculateFinalObjectOrOther()) { - node.setCanExit( - !isFinalObjectOrOtherSpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecFinalObject | SpecOther); - } else if (child.shouldSpeculateArrayOrOther()) { - node.setCanExit( - !isArrayOrOtherSpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecArray | SpecOther); + else if (child.shouldSpeculateNonStringCellOrOther()) { + node.setCanExit(true); + forNode(node.child1()).filter((SpecCell & ~SpecString) | SpecOther); } else if (child.shouldSpeculateInteger()) speculateInt32Unary(node); else if (child.shouldSpeculateNumber()) @@ -751,45 +728,32 @@ bool AbstractState::execute(unsigned indexInBlock) break; } - if (Node::shouldSpeculateFinalObject(left, right)) { - filter = SpecFinalObject; - checker = isFinalObjectSpeculation; - } else if (Node::shouldSpeculateArray(left, right)) { - filter = SpecArray; - checker = isArraySpeculation; - } else if (left.shouldSpeculateFinalObject() && right.shouldSpeculateFinalObjectOrOther()) { - node.setCanExit( - !isFinalObjectSpeculation(forNode(node.child1()).m_type) - || !isFinalObjectOrOtherSpeculation(forNode(node.child2()).m_type)); - forNode(node.child1()).filter(SpecFinalObject); - forNode(node.child2()).filter(SpecFinalObject | SpecOther); + if (left.shouldSpeculateString() || right.shouldSpeculateString()) { + node.setCanExit(false); break; - } else if (right.shouldSpeculateFinalObject() && left.shouldSpeculateFinalObjectOrOther()) { - node.setCanExit( - !isFinalObjectOrOtherSpeculation(forNode(node.child1()).m_type) - || !isFinalObjectSpeculation(forNode(node.child2()).m_type)); - forNode(node.child1()).filter(SpecFinalObject | SpecOther); - forNode(node.child2()).filter(SpecFinalObject); + } + if (left.shouldSpeculateNonStringCell() && right.shouldSpeculateNonStringCellOrOther()) { + node.setCanExit(true); + forNode(node.child1()).filter(SpecCell & ~SpecString); + forNode(node.child2()).filter((SpecCell & ~SpecString) | SpecOther); break; - } else if (left.shouldSpeculateArray() && right.shouldSpeculateArrayOrOther()) { - node.setCanExit( - !isArraySpeculation(forNode(node.child1()).m_type) - || !isArrayOrOtherSpeculation(forNode(node.child2()).m_type)); - forNode(node.child1()).filter(SpecArray); - forNode(node.child2()).filter(SpecArray | SpecOther); + } + if (left.shouldSpeculateNonStringCellOrOther() && right.shouldSpeculateNonStringCell()) { + node.setCanExit(true); + forNode(node.child1()).filter((SpecCell & ~SpecString) | SpecOther); + forNode(node.child2()).filter(SpecCell & ~SpecString); break; - } else if (right.shouldSpeculateArray() && left.shouldSpeculateArrayOrOther()) { - node.setCanExit( - !isArrayOrOtherSpeculation(forNode(node.child1()).m_type) - || !isArraySpeculation(forNode(node.child2()).m_type)); - forNode(node.child1()).filter(SpecArray | SpecOther); - forNode(node.child2()).filter(SpecArray); + } + if (left.shouldSpeculateNonStringCell() && right.shouldSpeculateNonStringCell()) { + node.setCanExit(true); + forNode(node.child1()).filter(SpecCell & ~SpecString); + forNode(node.child2()).filter(SpecCell & ~SpecString); break; - } else { - filter = SpecTop; - checker = isAnySpeculation; - clobberWorld(node.codeOrigin, indexInBlock); } + + filter = SpecTop; + checker = isAnySpeculation; + clobberWorld(node.codeOrigin, indexInBlock); } else { filter = SpecTop; checker = isAnySpeculation; @@ -837,22 +801,16 @@ bool AbstractState::execute(unsigned indexInBlock) speculateNumberBinary(node); break; } - if (Node::shouldSpeculateFinalObject( - m_graph[node.child1()], m_graph[node.child2()])) { - node.setCanExit( - !isFinalObjectSpeculation(forNode(node.child1()).m_type) - || !isFinalObjectSpeculation(forNode(node.child2()).m_type)); - forNode(node.child1()).filter(SpecFinalObject); - forNode(node.child2()).filter(SpecFinalObject); + Node& leftNode = m_graph[node.child1()]; + Node& rightNode = m_graph[node.child2()]; + if (leftNode.shouldSpeculateString() || rightNode.shouldSpeculateString()) { + node.setCanExit(false); break; } - if (Node::shouldSpeculateArray( - m_graph[node.child1()], m_graph[node.child2()])) { - node.setCanExit( - !isArraySpeculation(forNode(node.child1()).m_type) - || !isArraySpeculation(forNode(node.child2()).m_type)); - forNode(node.child1()).filter(SpecArray); - forNode(node.child2()).filter(SpecArray); + if (leftNode.shouldSpeculateNonStringCell() && rightNode.shouldSpeculateNonStringCell()) { + node.setCanExit(true); + forNode(node.child1()).filter((SpecCell & ~SpecString) | SpecOther); + forNode(node.child2()).filter((SpecCell & ~SpecString) | SpecOther); break; } node.setCanExit(false); @@ -887,55 +845,47 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).makeTop(); break; case Array::String: - forNode(node.child1()).filter(SpecString); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).set(SpecString); break; case Array::Arguments: - forNode(node.child1()).filter(SpecArguments); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).makeTop(); break; case Array::JSArray: + forNode(node.child2()).filter(SpecInt32); + forNode(nodeIndex).makeTop(); + break; case Array::JSArrayOutOfBounds: - // FIXME: We should have more conservative handling of the out-of-bounds - // case. - forNode(node.child1()).filter(SpecCell); forNode(node.child2()).filter(SpecInt32); + clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; case Array::Int8Array: - forNode(node.child1()).filter(SpecInt8Array); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).set(SpecInt32); break; case Array::Int16Array: - forNode(node.child1()).filter(SpecInt16Array); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).set(SpecInt32); break; case Array::Int32Array: - forNode(node.child1()).filter(SpecInt32Array); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).set(SpecInt32); break; case Array::Uint8Array: - forNode(node.child1()).filter(SpecUint8Array); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).set(SpecInt32); break; case Array::Uint8ClampedArray: - forNode(node.child1()).filter(SpecUint8ClampedArray); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).set(SpecInt32); break; case Array::Uint16Array: - forNode(node.child1()).filter(SpecUint16Array); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).set(SpecInt32); break; case Array::Uint32Array: - forNode(node.child1()).filter(SpecUint32Array); forNode(node.child2()).filter(SpecInt32); if (node.shouldSpeculateInteger()) forNode(nodeIndex).set(SpecInt32); @@ -943,12 +893,10 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).set(SpecDouble); break; case Array::Float32Array: - forNode(node.child1()).filter(SpecFloat32Array); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).set(SpecDouble); break; case Array::Float64Array: - forNode(node.child1()).filter(SpecFloat64Array); forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).set(SpecDouble); break; @@ -959,7 +907,6 @@ bool AbstractState::execute(unsigned indexInBlock) case PutByVal: case PutByValAlias: { node.setCanExit(true); - Edge child1 = m_graph.varArgChild(node, 0); Edge child2 = m_graph.varArgChild(node, 1); Edge child3 = m_graph.varArgChild(node, 2); switch (modeForPut(node.arrayMode())) { @@ -970,20 +917,16 @@ bool AbstractState::execute(unsigned indexInBlock) clobberWorld(node.codeOrigin, indexInBlock); break; case Array::JSArray: - forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); break; case Array::JSArrayOutOfBounds: - forNode(child1).filter(SpecCell); forNode(child2).filter(SpecInt32); clobberWorld(node.codeOrigin, indexInBlock); break; case Array::Arguments: - forNode(child1).filter(SpecArguments); forNode(child2).filter(SpecInt32); break; case Array::Int8Array: - forNode(child1).filter(SpecInt8Array); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -991,7 +934,6 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Int16Array: - forNode(child1).filter(SpecInt16Array); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -999,7 +941,6 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Int32Array: - forNode(child1).filter(SpecInt32Array); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -1007,7 +948,6 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Uint8Array: - forNode(child1).filter(SpecUint8Array); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -1015,7 +955,6 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Uint8ClampedArray: - forNode(child1).filter(SpecUint8ClampedArray); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -1023,7 +962,6 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Uint16Array: - forNode(child1).filter(SpecUint16Array); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -1031,7 +969,6 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Uint32Array: - forNode(child1).filter(SpecUint32Array); forNode(child2).filter(SpecInt32); if (m_graph[child3].shouldSpeculateInteger()) forNode(child3).filter(SpecInt32); @@ -1039,12 +976,10 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(child3).filter(SpecNumber); break; case Array::Float32Array: - forNode(child1).filter(SpecFloat32Array); forNode(child2).filter(SpecInt32); forNode(child3).filter(SpecNumber); break; case Array::Float64Array: - forNode(child1).filter(SpecFloat64Array); forNode(child2).filter(SpecInt32); forNode(child3).filter(SpecNumber); break; @@ -1057,13 +992,11 @@ bool AbstractState::execute(unsigned indexInBlock) case ArrayPush: node.setCanExit(true); - forNode(node.child1()).filter(SpecCell); forNode(nodeIndex).set(SpecNumber); break; case ArrayPop: node.setCanExit(true); - forNode(node.child1()).filter(SpecCell); forNode(nodeIndex).makeTop(); break; @@ -1102,14 +1035,9 @@ bool AbstractState::execute(unsigned indexInBlock) Node& child = m_graph[node.child1()]; if (child.shouldSpeculateBoolean()) speculateBooleanUnary(node); - else if (child.shouldSpeculateFinalObjectOrOther()) { - node.setCanExit( - !isFinalObjectOrOtherSpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecFinalObject | SpecOther); - } else if (child.shouldSpeculateArrayOrOther()) { - node.setCanExit( - !isArrayOrOtherSpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecArray | SpecOther); + else if (child.shouldSpeculateNonStringCellOrOther()) { + node.setCanExit(true); + forNode(node.child1()).filter((SpecCell & ~SpecString) | SpecOther); } else if (child.shouldSpeculateInteger()) speculateInt32Unary(node); else if (child.shouldSpeculateNumber()) @@ -1235,7 +1163,7 @@ bool AbstractState::execute(unsigned indexInBlock) case CreateActivation: node.setCanExit(false); - forNode(nodeIndex).set(m_graph.m_globalData.activationStructure.get()); + forNode(nodeIndex).set(m_codeBlock->globalObjectFor(node.codeOrigin)->activationStructure()); m_haveStructures = true; break; @@ -1347,80 +1275,8 @@ bool AbstractState::execute(unsigned indexInBlock) break; case GetArrayLength: - switch (node.arrayMode()) { - case Array::Undecided: - ASSERT_NOT_REACHED(); - break; - case Array::ForceExit: - m_isValid = false; - break; - case Array::Generic: - ASSERT_NOT_REACHED(); - break; - case Array::String: - node.setCanExit(!isStringSpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecString); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::JSArray: - node.setCanExit(true); - forNode(node.child1()).filter(SpecCell); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::JSArrayOutOfBounds: - ASSERT_NOT_REACHED(); - break; - case Array::Arguments: - node.setCanExit(true); - forNode(node.child1()).filter(SpecArguments); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::Int8Array: - node.setCanExit(!isInt8ArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecInt8Array); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::Int16Array: - node.setCanExit(!isInt16ArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecInt16Array); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::Int32Array: - node.setCanExit(!isInt32ArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecInt32Array); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::Uint8Array: - node.setCanExit(!isUint8ArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecUint8Array); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::Uint8ClampedArray: - node.setCanExit(!isUint8ClampedArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecUint8ClampedArray); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::Uint16Array: - node.setCanExit(!isUint16ArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecUint16Array); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::Uint32Array: - node.setCanExit(!isUint32ArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecUint32Array); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::Float32Array: - node.setCanExit(!isFloat32ArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecFloat32Array); - forNode(nodeIndex).set(SpecInt32); - break; - case Array::Float64Array: - node.setCanExit(!isFloat64ArraySpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecFloat64Array); - forNode(nodeIndex).set(SpecInt32); - break; - } + node.setCanExit(true); // Lies, but it's true for the common case of JSArray, so it's good enough. + forNode(nodeIndex).set(SpecInt32); break; case CheckStructure: @@ -1492,8 +1348,13 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(node.child1()).filter(SpecCell); forNode(nodeIndex).clear(); // The result is not a JS value. break; - case GetIndexedPropertyStorage: { - node.setCanExit(true); // Lies, but this is (almost) always followed by GetByVal, which does exit. So no point in trying to be more precise. + case CheckArray: { + if (modeAlreadyChecked(forNode(node.child1()), node.arrayMode())) { + m_foundConstants = true; + node.setCanExit(false); + break; + } + node.setCanExit(true); // Lies, but this is followed by operations (like GetByVal) that always exit, so there is no point in us trying to be clever here. switch (node.arrayMode()) { case Array::String: forNode(node.child1()).filter(SpecString); @@ -1504,6 +1365,9 @@ bool AbstractState::execute(unsigned indexInBlock) // CFA tracking of array mode speculations, but we don't have that, yet. forNode(node.child1()).filter(SpecCell); break; + case Array::Arguments: + forNode(node.child1()).filter(SpecArguments); + break; case Array::Int8Array: forNode(node.child1()).filter(SpecInt8Array); break; @@ -1535,6 +1399,19 @@ bool AbstractState::execute(unsigned indexInBlock) ASSERT_NOT_REACHED(); break; } + break; + } + case GetIndexedPropertyStorage: { + switch (node.arrayMode()) { + case Array::String: + // Strings are weird - we may spec fail if the string was a rope. That is of course + // stupid, and we should fix that, but for now let's at least be honest about it. + node.setCanExit(true); + break; + default: + node.setCanExit(false); + break; + } forNode(nodeIndex).clear(); break; } diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp index 2f535ba22..640a0a966 100644 --- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp @@ -312,6 +312,7 @@ public: case ForwardCheckStructure: case StructureTransitionWatchpoint: case ForwardStructureTransitionWatchpoint: + case CheckArray: // We don't care about these because if we get uses of the relevant // variable then we can safely get rid of these, too. This of course // relies on there not being any information transferred by the CFA @@ -476,7 +477,8 @@ public: case CheckStructure: case ForwardCheckStructure: case StructureTransitionWatchpoint: - case ForwardStructureTransitionWatchpoint: { + case ForwardStructureTransitionWatchpoint: + case CheckArray: { // We can just get rid of this node, if it references a phantom argument. if (!isOKToOptimize(m_graph[node.child1()])) break; diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp index ec4edc2e8..cd3944fb4 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp @@ -51,6 +51,11 @@ Array::Mode fromObserved(ArrayModes modes, bool makeSafe) } } +Array::Mode fromStructure(Structure* structure, bool makeSafe) +{ + return fromObserved(arrayModeFromStructure(structure), makeSafe); +} + Array::Mode refineArrayMode(Array::Mode arrayMode, SpeculatedType base, SpeculatedType index) { if (!base || !index) { @@ -64,17 +69,8 @@ Array::Mode refineArrayMode(Array::Mode arrayMode, SpeculatedType base, Speculat if (!isInt32Speculation(index) || !isCellSpeculation(base)) return Array::Generic; - // Pass through any array modes that would have been decided by the array profile, since - // the predictions of the inputs will not tell us anything useful that we didn't already - // get from the array profile. - switch (arrayMode) { - case Array::ForceExit: - case Array::JSArray: - case Array::JSArrayOutOfBounds: + if (arrayMode != Array::Undecided) return arrayMode; - default: - break; - } if (isStringSpeculation(base)) return Array::String; diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.h b/Source/JavaScriptCore/dfg/DFGArrayMode.h index 6ce62ae72..36a8637f5 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.h +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.h @@ -63,12 +63,25 @@ enum Mode { Array::Mode fromObserved(ArrayModes modes, bool makeSafe); +Array::Mode fromStructure(Structure*, bool makeSafe); + Array::Mode refineArrayMode(Array::Mode, SpeculatedType base, SpeculatedType index); bool modeAlreadyChecked(AbstractValue&, Array::Mode); const char* modeToString(Array::Mode); +inline bool modeIsJSArray(Array::Mode arrayMode) +{ + switch (arrayMode) { + case Array::JSArray: + case Array::JSArrayOutOfBounds: + return true; + default: + return false; + } +} + inline bool canCSEStorage(Array::Mode arrayMode) { switch (arrayMode) { @@ -82,6 +95,11 @@ inline bool canCSEStorage(Array::Mode arrayMode) } } +inline bool lengthNeedsStorage(Array::Mode arrayMode) +{ + return modeIsJSArray(arrayMode); +} + inline Array::Mode modeForPut(Array::Mode arrayMode) { switch (arrayMode) { @@ -115,7 +133,7 @@ inline bool modesCompatibleForStorageLoad(Array::Mode left, Array::Mode right) return false; } -inline bool modeSupportsLength(Array::Mode mode) +inline bool modeIsSpecific(Array::Mode mode) { switch (mode) { case Array::Undecided: @@ -127,6 +145,11 @@ inline bool modeSupportsLength(Array::Mode mode) } } +inline bool modeSupportsLength(Array::Mode mode) +{ + return modeIsSpecific(mode); +} + } } // namespace JSC::DFG #endif // ENABLE(DFG_JIT) diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeCache.h b/Source/JavaScriptCore/dfg/DFGByteCodeCache.h index f6a745c66..ab88e99e5 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeCache.h +++ b/Source/JavaScriptCore/dfg/DFGByteCodeCache.h @@ -141,7 +141,7 @@ public: } } - CodeBlock* get(const CodeBlockKey& key, ScopeChainNode* scope) + CodeBlock* get(const CodeBlockKey& key, JSScope* scope) { Map::iterator iter = m_map.find(key); if (iter != m_map.end()) diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index f9b1db9ab..b96b8d9a3 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -71,6 +71,7 @@ public: , m_inlineStackTop(0) , m_haveBuiltOperandMaps(false) , m_emptyJSValueIndex(UINT_MAX) + , m_currentInstruction(0) { ASSERT(m_profiledBlock); @@ -141,6 +142,9 @@ private: return getJSConstant(constant); } + if (operand == RegisterFile::Callee) + return getCallee(); + // Is this an argument? if (operandIsArgument(operand)) return getArgument(operand); @@ -364,11 +368,11 @@ private: InlineCallFrame* inlineCallFrame = stack->m_inlineCallFrame; if (!inlineCallFrame) break; - if (operand >= inlineCallFrame->stackOffset - RegisterFile::CallFrameHeaderSize) + if (operand >= static_cast<int>(inlineCallFrame->stackOffset - RegisterFile::CallFrameHeaderSize)) continue; if (operand == inlineCallFrame->stackOffset + CallFrame::thisArgumentOffset()) continue; - if (static_cast<unsigned>(operand) < inlineCallFrame->stackOffset - RegisterFile::CallFrameHeaderSize - inlineCallFrame->arguments.size()) + if (operand < static_cast<int>(inlineCallFrame->stackOffset - RegisterFile::CallFrameHeaderSize - inlineCallFrame->arguments.size())) continue; int argument = operandToArgument(operand - inlineCallFrame->stackOffset); return stack->m_argumentPositions[argument]; @@ -520,6 +524,11 @@ private: return resultIndex; } + NodeIndex getCallee() + { + return addToGraph(GetCallee); + } + // Helper functions to get/set the this value. NodeIndex getThis() { @@ -818,9 +827,14 @@ private: return getPrediction(m_graph.size(), m_currentProfilingIndex); } - Array::Mode getArrayModeWithoutOSRExit(Instruction* currentInstruction, NodeIndex base) + Array::Mode getArrayMode(ArrayProfile* profile) + { + profile->computeUpdatedPrediction(); + return fromObserved(profile->observedArrayModes(), false); + } + + Array::Mode getArrayModeAndEmitChecks(ArrayProfile* profile, NodeIndex base) { - ArrayProfile* profile = currentInstruction[4].u.arrayProfile; profile->computeUpdatedPrediction(); if (profile->hasDefiniteStructure()) addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(profile->expectedStructure())), base); @@ -838,16 +852,6 @@ private: return fromObserved(profile->observedArrayModes(), makeSafe); } - Array::Mode getArrayMode(Instruction* currentInstruction, NodeIndex base) - { - Array::Mode result = getArrayModeWithoutOSRExit(currentInstruction, base); - - if (result == Array::ForceExit) - addToGraph(ForceOSRExit); - - return result; - } - NodeIndex makeSafe(NodeIndex nodeIndex) { Node& node = m_graph[nodeIndex]; @@ -1130,7 +1134,10 @@ private: ASSERT(result >= FirstConstantRegisterIndex); return result; } - + + if (operand == RegisterFile::Callee) + return m_calleeVR; + return operand + m_inlineCallFrame->stackOffset; } }; @@ -1150,6 +1157,8 @@ private: // Cache of code blocks that we've generated bytecode for. ByteCodeCache<canInlineFunctionFor> m_codeBlockCache; + + Instruction* m_currentInstruction; }; #define NEXT_OPCODE(name) \ @@ -1553,7 +1562,10 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins if (argumentCountIncludingThis != 2) return false; - NodeIndex arrayPush = addToGraph(ArrayPush, OpInfo(0), OpInfo(prediction), get(registerOffset + argumentToOperand(0)), get(registerOffset + argumentToOperand(1))); + Array::Mode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); + if (!modeIsJSArray(arrayMode)) + return false; + NodeIndex arrayPush = addToGraph(ArrayPush, OpInfo(arrayMode), OpInfo(prediction), get(registerOffset + argumentToOperand(0)), get(registerOffset + argumentToOperand(1))); if (usesResult) set(resultOperand, arrayPush); @@ -1564,7 +1576,10 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins if (argumentCountIncludingThis != 1) return false; - NodeIndex arrayPop = addToGraph(ArrayPop, OpInfo(0), OpInfo(prediction), get(registerOffset + argumentToOperand(0))); + Array::Mode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); + if (!modeIsJSArray(arrayMode)) + return false; + NodeIndex arrayPop = addToGraph(ArrayPop, OpInfo(arrayMode), OpInfo(prediction), get(registerOffset + argumentToOperand(0))); if (usesResult) set(resultOperand, arrayPop); return true; @@ -1792,6 +1807,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) // Switch on the current bytecode opcode. Instruction* currentInstruction = instructionsBegin + m_currentIndex; + m_currentInstruction = currentInstruction; // Some methods want to use this, and we'd rather not thread it through calls. OpcodeID opcodeID = interpreter->getOpcodeID(currentInstruction->u.opcode); switch (opcodeID) { @@ -1830,10 +1846,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) } case op_create_this: { - if (m_inlineStackTop->m_inlineCallFrame) - set(currentInstruction[1].u.operand, addToGraph(CreateThis, getDirect(m_inlineStackTop->m_calleeVR))); - else - set(currentInstruction[1].u.operand, addToGraph(CreateThis, addToGraph(GetCallee))); + set(currentInstruction[1].u.operand, addToGraph(CreateThis, get(RegisterFile::Callee))); NEXT_OPCODE(op_create_this); } @@ -2177,7 +2190,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) SpeculatedType prediction = getPrediction(); NodeIndex base = get(currentInstruction[2].u.operand); - Array::Mode arrayMode = getArrayMode(currentInstruction, base); + Array::Mode arrayMode = getArrayModeAndEmitChecks(currentInstruction[4].u.arrayProfile, base); NodeIndex property = get(currentInstruction[3].u.operand); NodeIndex getByVal = addToGraph(GetByVal, OpInfo(arrayMode), OpInfo(prediction), base, property); set(currentInstruction[1].u.operand, getByVal); @@ -2188,7 +2201,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) case op_put_by_val: { NodeIndex base = get(currentInstruction[1].u.operand); - Array::Mode arrayMode = getArrayMode(currentInstruction, base); + Array::Mode arrayMode = getArrayModeAndEmitChecks(currentInstruction[4].u.arrayProfile, base); NodeIndex property = get(currentInstruction[2].u.operand); NodeIndex value = get(currentInstruction[3].u.operand); @@ -2196,6 +2209,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) addVarArgChild(base); addVarArgChild(property); addVarArgChild(value); + addVarArgChild(NoNode); // Leave room for property storage. addToGraph(Node::VarArg, PutByVal, OpInfo(arrayMode), OpInfo(0)); NEXT_OPCODE(op_put_by_val); @@ -2406,7 +2420,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[3].u.operand]; Identifier identifier = m_codeBlock->identifier(identifierNumber); - SymbolTableEntry entry = globalObject->symbolTable().get(identifier.impl()); + SymbolTableEntry entry = globalObject->symbolTable()->get(identifier.impl()); if (!entry.couldBeWatched()) { NodeIndex getGlobalVar = addToGraph( GetGlobalVar, @@ -2459,7 +2473,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) JSGlobalObject* globalObject = codeBlock->globalObject(); unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[4].u.operand]; Identifier identifier = m_codeBlock->identifier(identifierNumber); - SymbolTableEntry entry = globalObject->symbolTable().get(identifier.impl()); + SymbolTableEntry entry = globalObject->symbolTable()->get(identifier.impl()); if (!entry.couldBeWatched()) { addToGraph( PutGlobalVar, diff --git a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h index fd4e1cae0..5cd0baab2 100644 --- a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h +++ b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h @@ -456,6 +456,28 @@ public: setupTwoStubArgs<FPRInfo::argumentFPR0, FPRInfo::argumentFPR1>(arg1, arg2); } #elif CPU(ARM) +#if CPU(ARM_HARDFP) + ALWAYS_INLINE void setupArguments(FPRReg arg1) + { + moveDouble(arg1, FPRInfo::argumentFPR0); + } + + ALWAYS_INLINE void setupArguments(FPRReg arg1, FPRReg arg2) + { + if (arg2 != FPRInfo::argumentFPR0) { + moveDouble(arg1, FPRInfo::argumentFPR0); + moveDouble(arg2, FPRInfo::argumentFPR1); + } else if (arg1 != FPRInfo::argumentFPR1) { + moveDouble(arg2, FPRInfo::argumentFPR1); + moveDouble(arg1, FPRInfo::argumentFPR0); + } else { + // Swap arg1, arg2. + moveDouble(FPRInfo::argumentFPR0, ARMRegisters::d2); + moveDouble(FPRInfo::argumentFPR1, FPRInfo::argumentFPR0); + moveDouble(ARMRegisters::d2, FPRInfo::argumentFPR1); + } + } +#else ALWAYS_INLINE void setupArguments(FPRReg arg1) { assembler().vmov(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, arg1); @@ -466,6 +488,7 @@ public: assembler().vmov(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, arg1); assembler().vmov(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3, arg2); } +#endif // CPU(ARM_HARDFP) #else #error "DFG JIT not supported on this platform." #endif diff --git a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp index aecce83ed..e0d973992 100644 --- a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp @@ -659,8 +659,10 @@ private: if (node.flags() & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); - ++childIdx) - fixPossibleGetLocal(firstBlock, m_graph.m_varArgChildren[childIdx], changeRef); + ++childIdx) { + if (!!m_graph.m_varArgChildren[childIdx]) + fixPossibleGetLocal(firstBlock, m_graph.m_varArgChildren[childIdx], changeRef); + } } else if (!!node.child1()) { fixPossibleGetLocal(firstBlock, node.children.child1(), changeRef); if (!!node.child2()) { diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index dce57d520..b3681975d 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -35,9 +35,8 @@ namespace JSC { namespace DFG { class CSEPhase : public Phase { public: - CSEPhase(Graph& graph, OptimizationFixpointState fixpointState) + CSEPhase(Graph& graph) : Phase(graph, "common subexpression elimination") - , m_fixpointState(fixpointState) { // Replacements are used to implement local common subexpression elimination. m_replacements.resize(m_graph.size()); @@ -327,7 +326,7 @@ private: return false; } - bool checkStructureLoadElimination(const StructureSet& structureSet, NodeIndex child1) + bool checkStructureElimination(const StructureSet& structureSet, NodeIndex child1) { for (unsigned i = m_indexInBlock; i--;) { NodeIndex index = m_currentBlock->at(i); @@ -624,8 +623,37 @@ private: } return NoNode; } + + bool checkArrayElimination(NodeIndex child1, Array::Mode arrayMode) + { + for (unsigned i = m_indexInBlock; i--;) { + NodeIndex index = m_currentBlock->at(i); + if (index == child1) + break; - NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, bool hasIntegerIndexPrediction) + Node& node = m_graph[index]; + switch (node.op()) { + case PutByOffset: + case PutStructure: + // Changing the structure or putting to the storage cannot + // change the property storage pointer. + break; + + case CheckArray: + if (node.child1() == child1 && node.arrayMode() == arrayMode) + return true; + break; + + default: + if (m_graph.clobbersWorld(index)) + return false; + break; + } + } + return false; + } + + NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, Array::Mode arrayMode) { for (unsigned i = m_indexInBlock; i--;) { NodeIndex index = m_currentBlock->at(i); @@ -635,9 +663,7 @@ private: Node& node = m_graph[index]; switch (node.op()) { case GetIndexedPropertyStorage: { - SpeculatedType basePrediction = m_graph[node.child2()].prediction(); - bool nodeHasIntegerIndexPrediction = !(!(basePrediction & SpecInt32) && basePrediction); - if (node.child1() == child1 && hasIntegerIndexPrediction == nodeHasIntegerIndexPrediction) + if (node.child1() == child1 && node.arrayMode() == arrayMode) return index; break; } @@ -988,7 +1014,7 @@ private: ASSERT(replacement.variableAccessData() == variableAccessData); // FIXME: We should be able to remove SetLocals that can exit; we just need // to replace them with appropriate type checks. - if (m_fixpointState == FixpointNotConverged) { + if (m_graph.m_fixpointState == FixpointNotConverged) { // Need to be conservative at this time; if the SetLocal has any chance of performing // any speculations then we cannot do anything. if (variableAccessData->isCaptured()) { @@ -1077,7 +1103,7 @@ private: case PutGlobalVar: case PutGlobalVarCheck: - if (m_fixpointState == FixpointNotConverged) + if (m_graph.m_fixpointState == FixpointNotConverged) break; eliminate(globalVarStoreElimination(node.registerPointer())); break; @@ -1103,7 +1129,7 @@ private: case CheckStructure: case ForwardCheckStructure: - if (checkStructureLoadElimination(node.structureSet(), node.child1().index())) + if (checkStructureElimination(node.structureSet(), node.child1().index())) eliminate(); break; @@ -1114,7 +1140,7 @@ private: break; case PutStructure: - if (m_fixpointState == FixpointNotConverged) + if (m_graph.m_fixpointState == FixpointNotConverged) break; eliminate(putStructureStoreElimination(node.child1().index()), PhantomPutStructure); break; @@ -1124,10 +1150,13 @@ private: eliminate(); break; + case CheckArray: + if (checkArrayElimination(node.child1().index(), node.arrayMode())) + eliminate(); + break; + case GetIndexedPropertyStorage: { - SpeculatedType basePrediction = m_graph[node.child2()].prediction(); - bool nodeHasIntegerIndexPrediction = !(!(basePrediction & SpecInt32) && basePrediction); - setReplacement(getIndexedPropertyStorageLoadElimination(node.child1().index(), nodeHasIntegerIndexPrediction)); + setReplacement(getIndexedPropertyStorageLoadElimination(node.child1().index(), node.arrayMode())); break; } @@ -1140,7 +1169,7 @@ private: break; case PutByOffset: - if (m_fixpointState == FixpointNotConverged) + if (m_graph.m_fixpointState == FixpointNotConverged) break; eliminate(putByOffsetStoreElimination(m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber, node.child1().index())); break; @@ -1178,14 +1207,13 @@ private: unsigned m_indexInBlock; Vector<NodeIndex, 16> m_replacements; FixedArray<unsigned, LastNodeType> m_lastSeen; - OptimizationFixpointState m_fixpointState; bool m_changed; // Only tracks changes that have a substantive effect on other optimizations. }; -bool performCSE(Graph& graph, OptimizationFixpointState fixpointState) +bool performCSE(Graph& graph) { SamplingRegion samplingRegion("DFG CSE Phase"); - return runPhase<CSEPhase>(graph, fixpointState); + return runPhase<CSEPhase>(graph); } } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.h b/Source/JavaScriptCore/dfg/DFGCSEPhase.h index 7e33c2243..017bf5a4b 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.h +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.h @@ -41,7 +41,7 @@ class Graph; // a wide range of subexpression similarities. It's known to produce big wins // on a few benchmarks, and is relatively cheap to run. -bool performCSE(Graph&, OptimizationFixpointState); +bool performCSE(Graph&); } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h index 1a64a248c..ddbefd2d5 100644 --- a/Source/JavaScriptCore/dfg/DFGCommon.h +++ b/Source/JavaScriptCore/dfg/DFGCommon.h @@ -132,7 +132,7 @@ enum SpillRegistersMode { NeedToSpill, DontSpill }; enum NoResultTag { NoResult }; -enum OptimizationFixpointState { FixpointConverged, FixpointNotConverged }; +enum OptimizationFixpointState { BeforeFixpoint, FixpointNotConverged, FixpointConverged }; inline bool shouldShowDisassembly() { diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp index 68d5534e0..dfb62cbc4 100644 --- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp @@ -101,7 +101,16 @@ private: node.convertToStructureTransitionWatchpoint(structureValue.singleton()); break; } - + + case CheckArray: { + if (!modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode())) + break; + ASSERT(node.refCount() == 1); + node.setOpAndDefaultFlags(Phantom); + eliminated = true; + break; + } + default: break; } diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp index ccef65208..eb68fa344 100644 --- a/Source/JavaScriptCore/dfg/DFGDriver.cpp +++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp @@ -28,7 +28,7 @@ #include "JSObject.h" #include "JSString.h" -#include "ScopeChain.h" + #if ENABLE(DFG_JIT) @@ -41,7 +41,6 @@ #include "DFGFixupPhase.h" #include "DFGJITCompiler.h" #include "DFGPredictionPropagationPhase.h" -#include "DFGRedundantPhiEliminationPhase.h" #include "DFGStructureCheckHoistingPhase.h" #include "DFGValidate.h" #include "DFGVirtualRegisterAllocationPhase.h" @@ -117,6 +116,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo performFixup(dfg); performStructureCheckHoisting(dfg); unsigned cnt = 1; + dfg.m_fixpointState = FixpointNotConverged; for (;; ++cnt) { #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("DFG beginning optimization fixpoint iteration #%u.\n", cnt); @@ -126,13 +126,14 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo changed |= performConstantFolding(dfg); changed |= performArgumentsSimplification(dfg); changed |= performCFGSimplification(dfg); - changed |= performCSE(dfg, FixpointNotConverged); + changed |= performCSE(dfg); if (!changed) break; dfg.resetExitStates(); performFixup(dfg); } - performCSE(dfg, FixpointConverged); + dfg.m_fixpointState = FixpointConverged; + performCSE(dfg); #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("DFG optimization fixpoint converged in %u iterations.\n", cnt); #endif diff --git a/Source/JavaScriptCore/dfg/DFGFPRInfo.h b/Source/JavaScriptCore/dfg/DFGFPRInfo.h index e817ed396..5ee87bce1 100644 --- a/Source/JavaScriptCore/dfg/DFGFPRInfo.h +++ b/Source/JavaScriptCore/dfg/DFGFPRInfo.h @@ -122,6 +122,11 @@ public: // we'll return in d0 for simplicity. static const FPRReg returnValueFPR = ARMRegisters::d0; // fpRegT0 +#if CPU(ARM_HARDFP) + static const FPRReg argumentFPR0 = ARMRegisters::d0; // fpRegT0 + static const FPRReg argumentFPR1 = ARMRegisters::d1; // fpRegT1 +#endif + // FPRReg mapping is direct, the machine regsiter numbers can // be used directly as indices into the FPR RegisterBank. COMPILE_ASSERT(ARMRegisters::d0 == 0, d0_is_0); diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index fe7cae8a9..7700b4b86 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -74,6 +74,9 @@ private: switch (op) { case GetById: { + if (m_graph.m_fixpointState > BeforeFixpoint) + break; + Node* nodePtr = &node; if (!isInt32Speculation(m_graph[m_compileIndex].prediction())) @@ -90,8 +93,7 @@ private: fromObserved(arrayProfile->observedArrayModes(), false), m_graph[node.child1()].prediction(), m_graph[m_compileIndex].prediction()); - if (modeSupportsLength(arrayMode) - && arrayProfile->hasDefiniteStructure()) { + if (modeSupportsLength(arrayMode) && arrayProfile->hasDefiniteStructure()) { m_graph.ref(nodePtr->child1()); Node checkStructure(CheckStructure, nodePtr->codeOrigin, OpInfo(m_graph.addStructureSet(arrayProfile->expectedStructure())), nodePtr->child1().index()); checkStructure.ref(); @@ -113,17 +115,16 @@ private: nodePtr->clearFlags(NodeMustGenerate); m_graph.deref(m_compileIndex); nodePtr->setArrayMode(arrayMode); + + NodeIndex storage = checkArray(arrayMode, nodePtr->codeOrigin, nodePtr->child1().index(), lengthNeedsStorage, nodePtr->shouldGenerate()); + if (storage == NoNode) + break; + + nodePtr = &m_graph[m_compileIndex]; + nodePtr->children.child2() = Edge(storage); break; } case GetIndexedPropertyStorage: { - node.setArrayMode( - refineArrayMode( - node.arrayMode(), - m_graph[node.child1()].prediction(), - m_graph[node.child2()].prediction())); - // Predictions should only become more, rather than less, refined. Hence - // if we were ever able to CSE the storage pointer for this operation, - // then we should always continue to be able to do so. ASSERT(canCSEStorage(node.arrayMode())); break; } @@ -136,30 +137,19 @@ private: m_graph[node.child1()].prediction(), m_graph[node.child2()].prediction())); - if (canCSEStorage(node.arrayMode())) { - if (node.child3()) { - ASSERT(m_graph[node.child3()].op() == GetIndexedPropertyStorage); - ASSERT(modesCompatibleForStorageLoad(m_graph[node.child3()].arrayMode(), node.arrayMode())); - } else { - // Make sure we don't use the node reference after we do the append. - Node getIndexedPropertyStorage( - GetIndexedPropertyStorage, node.codeOrigin, OpInfo(node.arrayMode()), - node.child1().index(), node.child2().index()); - NodeIndex getIndexedPropertyStorageIndex = m_graph.size(); - node.children.child3() = Edge(getIndexedPropertyStorageIndex); - m_graph.append(getIndexedPropertyStorage); - m_graph.ref(getIndexedPropertyStorageIndex); // Once because it's MustGenerate. - m_graph.ref(getIndexedPropertyStorageIndex); // And again because it's referenced from the GetByVal. - m_insertionSet.append(m_indexInBlock, getIndexedPropertyStorageIndex); - } - } else { - // See above. Continued fixup of the graph should not regress our ability - // to speculate. - ASSERT(!node.child3()); - } + blessArrayOperation(node.child1(), 2); + break; + } + + case ArrayPush: { + blessArrayOperation(node.child1(), 2); break; } + case ArrayPop: { + blessArrayOperation(node.child1(), 1); + } + case ValueToInt32: { if (m_graph[node.child1()].shouldSpeculateNumber() && node.mustGenerate()) { @@ -330,11 +320,18 @@ private: Edge child1 = m_graph.varArgChild(node, 0); Edge child2 = m_graph.varArgChild(node, 1); Edge child3 = m_graph.varArgChild(node, 2); + node.setArrayMode( refineArrayMode( - node.arrayMode(), m_graph[child1].prediction(), m_graph[child2].prediction())); + node.arrayMode(), + m_graph[child1].prediction(), + m_graph[child2].prediction())); + + blessArrayOperation(child1, 3); - switch (modeForPut(node.arrayMode())) { + Node* nodePtr = &m_graph[m_compileIndex]; + + switch (modeForPut(nodePtr->arrayMode())) { case Array::Int8Array: case Array::Int16Array: case Array::Int32Array: @@ -368,6 +365,59 @@ private: #endif } + NodeIndex checkArray(Array::Mode arrayMode, CodeOrigin codeOrigin, NodeIndex array, bool (*storageCheck)(Array::Mode) = canCSEStorage, bool shouldGenerate = true) + { + ASSERT(modeIsSpecific(arrayMode)); + + m_graph.ref(array); + Node checkArray(CheckArray, codeOrigin, OpInfo(arrayMode), array); + checkArray.ref(); + NodeIndex checkArrayIndex = m_graph.size(); + m_graph.append(checkArray); + m_insertionSet.append(m_indexInBlock, checkArrayIndex); + + if (!storageCheck(arrayMode)) + return NoNode; + + if (shouldGenerate) + m_graph.ref(array); + Node getIndexedPropertyStorage( + GetIndexedPropertyStorage, codeOrigin, OpInfo(arrayMode), array); + if (shouldGenerate) + getIndexedPropertyStorage.ref(); + NodeIndex getIndexedPropertyStorageIndex = m_graph.size(); + m_graph.append(getIndexedPropertyStorage); + m_insertionSet.append(m_indexInBlock, getIndexedPropertyStorageIndex); + + return getIndexedPropertyStorageIndex; + } + + void blessArrayOperation(Edge base, unsigned storageChildIdx) + { + if (m_graph.m_fixpointState > BeforeFixpoint) + return; + + Node* nodePtr = &m_graph[m_compileIndex]; + + if (nodePtr->arrayMode() == Array::ForceExit) { + Node forceExit(ForceOSRExit, nodePtr->codeOrigin); + forceExit.ref(); + NodeIndex forceExitIndex = m_graph.size(); + m_graph.append(forceExit); + m_insertionSet.append(m_indexInBlock, forceExitIndex); + return; + } + + if (!modeIsSpecific(nodePtr->arrayMode())) + return; + + NodeIndex storage = checkArray(nodePtr->arrayMode(), nodePtr->codeOrigin, base.index()); + if (storage == NoNode) + return; + + m_graph.child(m_graph[m_compileIndex], storageChildIdx) = Edge(storage); + } + void fixIntEdge(Edge& edge) { Node& node = m_graph[edge]; diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp index 8e80ff2fc..f4d260b9e 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.cpp +++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp @@ -40,6 +40,18 @@ static const char* dfgOpNames[] = { #undef STRINGIZE_DFG_OP_ENUM }; +Graph::Graph(JSGlobalData& globalData, CodeBlock* codeBlock, unsigned osrEntryBytecodeIndex, const Operands<JSValue>& mustHandleValues) + : m_globalData(globalData) + , m_codeBlock(codeBlock) + , m_profiledBlock(codeBlock->alternative()) + , m_hasArguments(false) + , m_osrEntryBytecodeIndex(osrEntryBytecodeIndex) + , m_mustHandleValues(mustHandleValues) + , m_fixpointState(BeforeFixpoint) +{ + ASSERT(m_profiledBlock); +} + const char *Graph::opName(NodeType op) { return dfgOpNames[op]; @@ -179,6 +191,8 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) dataLog(", "); else hasPrinted = true; + if (!m_varArgChildren[childIdx]) + continue; dataLog("%s@%u%s", useKindToString(m_varArgChildren[childIdx].useKind()), m_varArgChildren[childIdx].index(), @@ -227,7 +241,7 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) hasPrinted = true; } if (node.hasIdentifier()) { - dataLog("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), m_codeBlock->identifier(node.identifierNumber()).ustring().utf8().data()); + dataLog("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), m_codeBlock->identifier(node.identifierNumber()).string().utf8().data()); hasPrinted = true; } if (node.hasStructureSet()) { @@ -246,7 +260,7 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) } if (node.hasStorageAccessData()) { StorageAccessData& storageAccessData = m_storageAccessData[node.storageAccessDataIndex()]; - dataLog("%sid%u{%s}", hasPrinted ? ", " : "", storageAccessData.identifierNumber, m_codeBlock->identifier(storageAccessData.identifierNumber).ustring().utf8().data()); + dataLog("%sid%u{%s}", hasPrinted ? ", " : "", storageAccessData.identifierNumber, m_codeBlock->identifier(storageAccessData.identifierNumber).string().utf8().data()); dataLog(", %lu", static_cast<unsigned long>(storageAccessData.offset)); hasPrinted = true; @@ -392,8 +406,10 @@ void Graph::dump() if (_node.flags() & NodeHasVarArgs) { \ for (unsigned _childIdx = _node.firstChild(); \ _childIdx < _node.firstChild() + _node.numChildren(); \ - _childIdx++) \ - thingToDo(m_varArgChildren[_childIdx]); \ + _childIdx++) { \ + if (!!m_varArgChildren[_childIdx]) \ + thingToDo(m_varArgChildren[_childIdx]); \ + } \ } else { \ if (!_node.child1()) { \ ASSERT(!_node.child2() \ @@ -483,6 +499,8 @@ void Graph::collectGarbage() for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx) { + if (!m_varArgChildren[childIdx]) + continue; NodeIndex childNodeIndex = m_varArgChildren[childIdx].index(); if (!at(childNodeIndex).ref()) continue; diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h index ba5d86f81..70cbbaf07 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.h +++ b/Source/JavaScriptCore/dfg/DFGGraph.h @@ -53,14 +53,6 @@ namespace DFG { struct StorageAccessData { size_t offset; unsigned identifierNumber; - - // NOTE: the offset and identifierNumber do not by themselves - // uniquely identify a property. The identifierNumber and a - // Structure* do. If those two match, then the offset should - // be the same, as well. For any Node that has a StorageAccessData, - // it is possible to retrieve the Structure* by looking at the - // first child. It should be a CheckStructure, which has the - // Structure*. }; struct ResolveGlobalData { @@ -76,16 +68,7 @@ struct ResolveGlobalData { // Nodes that are 'dead' remain in the vector with refCount 0. class Graph : public Vector<Node, 64> { public: - Graph(JSGlobalData& globalData, CodeBlock* codeBlock, unsigned osrEntryBytecodeIndex, const Operands<JSValue>& mustHandleValues) - : m_globalData(globalData) - , m_codeBlock(codeBlock) - , m_profiledBlock(codeBlock->alternative()) - , m_hasArguments(false) - , m_osrEntryBytecodeIndex(osrEntryBytecodeIndex) - , m_mustHandleValues(mustHandleValues) - { - ASSERT(m_profiledBlock); - } + Graph(JSGlobalData&, CodeBlock*, unsigned osrEntryBytecodeIndex, const Operands<JSValue>& mustHandleValues); using Vector<Node, 64>::operator[]; using Vector<Node, 64>::at; @@ -585,8 +568,10 @@ public: if (node.flags() & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); - childIdx++) - vote(m_varArgChildren[childIdx], ballot); + childIdx++) { + if (!!m_varArgChildren[childIdx]) + vote(m_varArgChildren[childIdx], ballot); + } return; } @@ -608,8 +593,10 @@ public: NodeIndex nodeIndex = block[indexInBlock]; Node& node = at(nodeIndex); if (node.flags() & NodeHasVarArgs) { - for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx) - compareAndSwap(m_varArgChildren[childIdx], oldThing, newThing, node.shouldGenerate()); + for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx) { + if (!!m_varArgChildren[childIdx]) + compareAndSwap(m_varArgChildren[childIdx], oldThing, newThing, node.shouldGenerate()); + } continue; } if (!node.child1()) @@ -685,6 +672,8 @@ public: unsigned m_parameterSlots; unsigned m_osrEntryBytecodeIndex; Operands<JSValue> m_mustHandleValues; + + OptimizationFixpointState m_fixpointState; private: void handleSuccessor(Vector<BlockIndex, 16>& worklist, BlockIndex blockIndex, BlockIndex successorIndex); diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index 7ca4d8d48..fb31f935e 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -743,6 +743,9 @@ struct Node { case GetByVal: case StringCharAt: case StringCharCodeAt: + case CheckArray: + case ArrayPush: + case ArrayPop: return true; default: return false; @@ -755,10 +758,13 @@ struct Node { return static_cast<Array::Mode>(m_opInfo); } - void setArrayMode(Array::Mode arrayMode) + bool setArrayMode(Array::Mode arrayMode) { ASSERT(hasArrayMode()); + if (this->arrayMode() == arrayMode) + return false; m_opInfo = arrayMode; + return true; } bool hasVirtualRegister() @@ -910,12 +916,27 @@ struct Node { { return isBooleanSpeculation(prediction()); } - + + bool shouldSpeculateString() + { + return isStringSpeculation(prediction()); + } + bool shouldSpeculateFinalObject() { return isFinalObjectSpeculation(prediction()); } + bool shouldSpeculateNonStringCell() + { + return isNonStringCellSpeculation(prediction()); + } + + bool shouldSpeculateNonStringCellOrOther() + { + return isNonStringCellOrOtherSpeculation(prediction()); + } + bool shouldSpeculateFinalObjectOrOther() { return isFinalObjectOrOtherSpeculation(prediction()); diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index ee5ad9013..d86b9b356 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -138,7 +138,8 @@ namespace JSC { namespace DFG { macro(AllocatePropertyStorage, NodeMustGenerate | NodeDoesNotExit | NodeResultStorage) \ macro(ReallocatePropertyStorage, NodeMustGenerate | NodeDoesNotExit | NodeResultStorage) \ macro(GetPropertyStorage, NodeResultStorage) \ - macro(GetIndexedPropertyStorage, NodeMustGenerate | NodeResultStorage) \ + macro(CheckArray, NodeMustGenerate) \ + macro(GetIndexedPropertyStorage, NodeResultStorage) \ macro(GetByOffset, NodeResultJS) \ macro(PutByOffset, NodeMustGenerate) \ macro(GetArrayLength, NodeResultInt32) \ diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index b5c3b961b..824a0a37a 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -39,11 +39,13 @@ #include "JITExceptions.h" #include "JSActivation.h" #include "JSGlobalData.h" -#include "JSStaticScopeObject.h" +#include "JSNameScope.h" #include "NameInstance.h" #include "Operations.h" #include <wtf/InlineASM.h> +#if ENABLE(JIT) + #if ENABLE(DFG_JIT) #if CPU(X86_64) @@ -407,6 +409,20 @@ EncodedJSValue DFG_OPERATION operationGetByValCell(ExecState* exec, JSCell* base return JSValue::encode(JSValue(base).get(exec, ident)); } +EncodedJSValue DFG_OPERATION operationGetByValArrayInt(ExecState* exec, JSArray* base, int32_t index) +{ + JSGlobalData* globalData = &exec->globalData(); + NativeCallFrameTracer tracer(globalData, exec); + + if (index < 0) { + // Go the slowest way possible becase negative indices don't use indexed storage. + return JSValue::encode(JSValue(base).get(exec, Identifier::from(exec, index))); + } + + // Use this since we know that the value is out of bounds. + return JSValue::encode(JSValue(base).get(exec, index)); +} + EncodedJSValue DFG_OPERATION operationGetById(ExecState* exec, EncodedJSValue base, Identifier* propertyName) { JSGlobalData* globalData = &exec->globalData(); @@ -575,6 +591,14 @@ EncodedJSValue DFG_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue return JSValue::encode(jsNumber(array->length())); } +EncodedJSValue DFG_OPERATION operationArrayPop(ExecState* exec, JSArray* array) +{ + JSGlobalData* globalData = &exec->globalData(); + NativeCallFrameTracer tracer(globalData, exec); + + return JSValue::encode(array->pop(exec)); +} + EncodedJSValue DFG_OPERATION operationRegExpExec(ExecState* exec, JSCell* base, JSCell* argument) { JSGlobalData& globalData = exec->globalData(); @@ -603,14 +627,6 @@ size_t DFG_OPERATION operationRegExpTest(ExecState* exec, JSCell* base, JSCell* return asRegExpObject(base)->test(exec, input); } -EncodedJSValue DFG_OPERATION operationArrayPop(ExecState* exec, JSArray* array) -{ - JSGlobalData* globalData = &exec->globalData(); - NativeCallFrameTracer tracer(globalData, exec); - - return JSValue::encode(array->pop(exec)); -} - void DFG_OPERATION operationPutByIdStrict(ExecState* exec, EncodedJSValue encodedValue, JSCell* base, Identifier* propertyName) { JSGlobalData* globalData = &exec->globalData(); @@ -899,7 +915,7 @@ static void* handleHostCall(ExecState* execCallee, JSValue callee, CodeSpecializ ExecState* exec = execCallee->callerFrame(); JSGlobalData* globalData = &exec->globalData(); - execCallee->setScopeChain(exec->scopeChain()); + execCallee->setScope(exec->scope()); execCallee->setCodeBlock(0); if (kind == CodeForCall) { @@ -957,7 +973,7 @@ inline char* linkFor(ExecState* execCallee, CodeSpecializationKind kind) return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind)); JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell); - execCallee->setScopeChain(callee->scopeUnchecked()); + execCallee->setScope(callee->scopeUnchecked()); ExecutableBase* executable = callee->executable(); MacroAssemblerCodePtr codePtr; @@ -1007,7 +1023,7 @@ inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind) return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind)); JSFunction* function = jsCast<JSFunction*>(calleeAsFunctionCell); - execCallee->setScopeChain(function->scopeUnchecked()); + execCallee->setScope(function->scopeUnchecked()); ExecutableBase* executable = function->executable(); if (UNLIKELY(!executable->hasJITCodeFor(kind))) { FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable); @@ -1039,20 +1055,7 @@ EncodedJSValue DFG_OPERATION operationResolve(ExecState* exec, Identifier* prope { JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); - - ScopeChainNode* scopeChain = exec->scopeChain(); - ScopeChainIterator iter = scopeChain->begin(); - ScopeChainIterator end = scopeChain->end(); - ASSERT(iter != end); - - do { - JSObject* record = iter->get(); - PropertySlot slot(record); - if (record->getPropertySlot(exec, *propertyName, slot)) - return JSValue::encode(slot.getValue(exec, *propertyName)); - } while (++iter != end); - - return throwVMError(exec, createUndefinedVariableError(exec, *propertyName)); + return JSValue::encode(JSScope::resolve(exec, *propertyName)); } EncodedJSValue DFG_OPERATION operationResolveBase(ExecState* exec, Identifier* propertyName) @@ -1060,7 +1063,7 @@ EncodedJSValue DFG_OPERATION operationResolveBase(ExecState* exec, Identifier* p JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); - return JSValue::encode(resolveBase(exec, *propertyName, exec->scopeChain(), false)); + return JSValue::encode(JSScope::resolveBase(exec, *propertyName, false)); } EncodedJSValue DFG_OPERATION operationResolveBaseStrictPut(ExecState* exec, Identifier* propertyName) @@ -1068,30 +1071,15 @@ EncodedJSValue DFG_OPERATION operationResolveBaseStrictPut(ExecState* exec, Iden JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); - JSValue base = resolveBase(exec, *propertyName, exec->scopeChain(), true); - if (!base) - throwError(exec, createErrorForInvalidGlobalAssignment(exec, propertyName->ustring())); - return JSValue::encode(base); + return JSValue::encode(JSScope::resolveBase(exec, *propertyName, true)); } EncodedJSValue DFG_OPERATION operationResolveGlobal(ExecState* exec, GlobalResolveInfo* resolveInfo, JSGlobalObject* globalObject, Identifier* propertyName) { JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); - - PropertySlot slot(globalObject); - if (globalObject->getPropertySlot(exec, *propertyName, slot)) { - JSValue result = slot.getValue(exec, *propertyName); - - if (slot.isCacheableValue() && !globalObject->structure()->isUncacheableDictionary() && slot.slotBase() == globalObject) { - resolveInfo->structure.set(exec->globalData(), exec->codeBlock()->ownerExecutable(), globalObject->structure()); - resolveInfo->offset = slot.cachedOffset(); - } - return JSValue::encode(result); - } - - return throwVMError(exec, createUndefinedVariableError(exec, *propertyName)); + return JSValue::encode(JSScope::resolveGlobal(exec, *propertyName, globalObject, &resolveInfo->structure, &resolveInfo->offset)); } EncodedJSValue DFG_OPERATION operationToPrimitive(ExecState* exec, EncodedJSValue value) @@ -1154,7 +1142,7 @@ JSCell* DFG_OPERATION operationCreateActivation(ExecState* exec) NativeCallFrameTracer tracer(&globalData, exec); JSActivation* activation = JSActivation::create( globalData, exec, static_cast<FunctionExecutable*>(exec->codeBlock()->ownerExecutable())); - exec->setScopeChain(exec->scopeChain()->push(activation)); + exec->setScope(activation); return activation; } @@ -1255,7 +1243,7 @@ JSCell* DFG_OPERATION operationNewFunction(ExecState* exec, JSCell* functionExec ASSERT(functionExecutable->inherits(&FunctionExecutable::s_info)); JSGlobalData& globalData = exec->globalData(); NativeCallFrameTracer tracer(&globalData, exec); - return static_cast<FunctionExecutable*>(functionExecutable)->make(exec, exec->scopeChain()); + return JSFunction::create(exec, static_cast<FunctionExecutable*>(functionExecutable), exec->scope()); } JSCell* DFG_OPERATION operationNewFunctionExpression(ExecState* exec, JSCell* functionExecutableAsCell) @@ -1263,14 +1251,7 @@ JSCell* DFG_OPERATION operationNewFunctionExpression(ExecState* exec, JSCell* fu ASSERT(functionExecutableAsCell->inherits(&FunctionExecutable::s_info)); FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(functionExecutableAsCell); - JSFunction *function = functionExecutable->make(exec, exec->scopeChain()); - if (!functionExecutable->name().isNull()) { - JSStaticScopeObject* functionScopeObject = - JSStaticScopeObject::create( - exec, functionExecutable->name(), function, ReadOnly | DontDelete); - function->setScope(exec->globalData(), function->scope()->push(functionScopeObject)); - } - return function; + return JSFunction::create(exec, functionExecutable, exec->scope()); } size_t DFG_OPERATION operationIsObject(ExecState* exec, EncodedJSValue value) @@ -1416,7 +1397,7 @@ extern "C" void DFG_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock) } // extern "C" } } // namespace JSC::DFG -#endif +#endif // ENABLE(DFG_JIT) #if COMPILER(GCC) @@ -1478,3 +1459,4 @@ extern "C" EncodedJSValue HOST_CALL_RETURN_VALUE_OPTION getHostCallReturnValueWi #endif // COMPILER(GCC) +#endif // ENABLE(JIT) diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h index 455c2bcc3..82babe875 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.h +++ b/Source/JavaScriptCore/dfg/DFGOperations.h @@ -62,6 +62,7 @@ extern "C" { */ typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_E)(ExecState*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EA)(ExecState*, JSArray*); +typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EAZ)(ExecState*, JSArray*, int32_t); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECI)(ExecState*, JSCell*, Identifier*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECJ)(ExecState*, JSCell*, EncodedJSValue); @@ -116,6 +117,7 @@ EncodedJSValue DFG_OPERATION operationValueAdd(ExecState*, EncodedJSValue encode EncodedJSValue DFG_OPERATION operationValueAddNotNumber(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationGetByVal(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationGetByValCell(ExecState*, JSCell*, EncodedJSValue encodedProperty) WTF_INTERNAL; +EncodedJSValue DFG_OPERATION operationGetByValArrayInt(ExecState*, JSArray*, int32_t) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationGetById(ExecState*, EncodedJSValue, Identifier*) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationGetByIdBuildList(ExecState*, EncodedJSValue, Identifier*) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationGetByIdProtoBuildList(ExecState*, EncodedJSValue, Identifier*) WTF_INTERNAL; diff --git a/Source/JavaScriptCore/dfg/DFGPhase.h b/Source/JavaScriptCore/dfg/DFGPhase.h index 80fd6914a..a73d26baf 100644 --- a/Source/JavaScriptCore/dfg/DFGPhase.h +++ b/Source/JavaScriptCore/dfg/DFGPhase.h @@ -95,13 +95,6 @@ bool runPhase(Graph& graph) return runAndLog(phase); } -template<typename PhaseType, typename ArgumentType1> -bool runPhase(Graph& graph, ArgumentType1 arg1) -{ - PhaseType phase(graph, arg1); - return runAndLog(phase); -} - } } // namespace JSC::DFG #endif // ENABLE(DFG_JIT) diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index 258d1199a..af57ab8fe 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -618,7 +618,8 @@ private: case GetMyArgumentsLength: case GetMyArgumentByVal: case PhantomPutStructure: - case PhantomArguments: { + case PhantomArguments: + case CheckArray: { // This node should never be visible at this stage of compilation. It is // inserted by fixup(), which follows this phase. ASSERT_NOT_REACHED(); @@ -703,8 +704,10 @@ private: if (node.flags() & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); - childIdx++) - changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsValue); + childIdx++) { + if (!!m_graph.m_varArgChildren[childIdx]) + changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsValue); + } } else { if (!node.child1()) return changed; diff --git a/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp b/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp deleted file mode 100644 index 32e4ef157..000000000 --- a/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2012 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "DFGRedundantPhiEliminationPhase.h" - -#if ENABLE(DFG_JIT) - -#include "DFGGraph.h" - -namespace JSC { namespace DFG { - -class RedundantPhiEliminationPhase : public Phase { -public: - RedundantPhiEliminationPhase(Graph& graph) - : Phase(graph, "redundant phi elimination") - { - } - - bool run() - { - bool changed = false; - do { - changed = fixupPhis(); - } while (changed); - - updateBlockVariableInformation(); - - // Update the Phi references from non-Phi nodes, e.g., the GetLocals. - for (NodeIndex index = 0; index < m_graph.size(); ++index) { - Node& node = m_graph[index]; - - if (!node.shouldGenerate()) - continue; - - switch (node.op()) { - case GetLocal: - replacePhiChild(node, 0); - break; - default: - break; - } - } - - return true; - } - -private: - NodeIndex getRedundantReplacement(NodeIndex phi) - { - NodeIndex child1 = m_graph[phi].child1().indexUnchecked(); - NodeIndex candidate = child1 == phi ? NoNode : child1; - - NodeIndex child2 = m_graph[phi].child2().indexUnchecked(); - if (candidate != NoNode) { - if (child2 != NoNode && child2 != candidate && child2 != phi) - return NoNode; - } else if (child2 != phi) - candidate = child2; - - NodeIndex child3 = m_graph[phi].child3().indexUnchecked(); - if (candidate != NoNode) { - if (child3 != NoNode && child3 != candidate && child3 != phi) - return NoNode; - } else if (child3 != phi) - candidate = child3; - - return candidate; - } - - bool replacePhiChild(Node& node, unsigned childIndex) - { - ASSERT(childIndex < 3); - - bool replaced = false; - NodeIndex child = node.children.child(childIndex).indexUnchecked(); - if (child != NoNode && m_graph[child].op() == Phi) { - NodeIndex childReplacement = getRedundantReplacement(child); - if (childReplacement != NoNode) { - node.children.child(childIndex).setIndex(childReplacement); - replaced = true; - if (node.refCount()) { - m_graph[childReplacement].ref(); - m_graph.deref(child); - } - } - } - return replaced; - } - - bool fixupPhis() - { - bool changed = false; - - for (BlockIndex block = 0; block < m_graph.m_blocks.size(); ++block) { - Vector<NodeIndex>& phis = m_graph.m_blocks[block]->phis; - - for (size_t i = 0; i < phis.size(); ++i) { - NodeIndex phi = phis[i]; - Node& phiNode = m_graph[phi]; - - changed |= (replacePhiChild(phiNode, 0) && phiNode.refCount()); - changed |= (replacePhiChild(phiNode, 1) && phiNode.refCount()); - changed |= (replacePhiChild(phiNode, 2) && phiNode.refCount()); - } - } - - return changed; - } - - void updateBlockVariableInformation() - { - // Redundant Phi nodes are eliminated, we need to update - // the variable information if it references them. - for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { - BasicBlock* basicBlock = m_graph.m_blocks[blockIndex].get(); - - for (size_t arg = 0; arg < basicBlock->variablesAtHead.numberOfArguments(); ++arg) { - NodeIndex nodeIndex = basicBlock->variablesAtHead.argument(arg); - if (nodeIndex != NoNode && m_graph[nodeIndex].op() == Phi && !m_graph[nodeIndex].refCount()) { - NodeIndex replacement = getRedundantReplacement(nodeIndex); - if (replacement != NoNode) { - // This argument must be unused in this block. - ASSERT(basicBlock->variablesAtTail.argument(arg) == nodeIndex); - basicBlock->variablesAtHead.argument(arg) = replacement; - basicBlock->variablesAtTail.argument(arg) = replacement; - } - } - } - - for (size_t local = 0; local < basicBlock->variablesAtHead.numberOfLocals(); ++local) { - NodeIndex nodeIndex = basicBlock->variablesAtHead.local(local); - if (nodeIndex != NoNode && m_graph[nodeIndex].op() == Phi && !m_graph[nodeIndex].refCount()) { - NodeIndex replacement = getRedundantReplacement(nodeIndex); - if (replacement != NoNode) { - // This local variable must be unused in this block. - ASSERT(basicBlock->variablesAtTail.local(local) == nodeIndex); - basicBlock->variablesAtHead.local(local) = replacement; - basicBlock->variablesAtTail.local(local) = replacement; - } - } - } - } - } - -}; - -bool performRedundantPhiElimination(Graph& graph) -{ - SamplingRegion samplingRegion("DFG Redundant Phi Elimination Phase"); - return runPhase<RedundantPhiEliminationPhase>(graph); -} - -} } // namespace JSC::DFG - -#endif // ENABLE(DFG_JIT) diff --git a/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.h b/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.h deleted file mode 100644 index fd6634a88..000000000 --- a/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2012 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef DFGRedundantPhiEliminationPhase_h -#define DFGRedundantPhiEliminationPhase_h - -#include <wtf/Platform.h> - -#if ENABLE(DFG_JIT) - -#include "DFGPhase.h" - -namespace JSC { namespace DFG { - -class Graph; - -// We inserted many can-be-redundant Phi nodes when building the graph. -// This phase will just remove them. - -bool performRedundantPhiElimination(Graph&); - -} } // namespace JSC::DFG - -#endif // ENABLE(DFG_JIT) - -#endif // DFGRedundantPhiEliminationPhase_h - diff --git a/Source/JavaScriptCore/dfg/DFGRepatch.cpp b/Source/JavaScriptCore/dfg/DFGRepatch.cpp index 23e8db03b..1af4999e8 100644 --- a/Source/JavaScriptCore/dfg/DFGRepatch.cpp +++ b/Source/JavaScriptCore/dfg/DFGRepatch.cpp @@ -252,8 +252,12 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier } MacroAssembler::JumpList failureCases; - - failureCases.append(stubJit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + + stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSCell::structureOffset()), scratchGPR); + failureCases.append(stubJit.branchPtr( + MacroAssembler::NotEqual, + MacroAssembler::Address(scratchGPR, Structure::classInfoOffset()), + MacroAssembler::TrustedImmPtr(&JSArray::s_info))); stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), scratchGPR); stubJit.load32(MacroAssembler::Address(scratchGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), scratchGPR); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index d74207420..c9c07da80 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -295,20 +295,23 @@ const TypedArrayDescriptor* SpeculativeJIT::typedArrayDescriptor(Array::Mode arr } } -const TypedArrayDescriptor* SpeculativeJIT::speculateArray(Array::Mode arrayMode, Edge edge, GPRReg baseReg) +void SpeculativeJIT::checkArray(Node& node) { - const TypedArrayDescriptor* result = typedArrayDescriptor(arrayMode); + ASSERT(modeIsSpecific(node.arrayMode())); - if (modeAlreadyChecked(m_state.forNode(edge), arrayMode)) - return result; + SpeculateCellOperand base(this, node.child1()); + GPRReg baseReg = base.gpr(); + + const TypedArrayDescriptor* result = typedArrayDescriptor(node.arrayMode()); + + if (modeAlreadyChecked(m_state.forNode(node.child1()), node.arrayMode())) { + noResult(m_compileIndex); + return; + } const ClassInfo* expectedClassInfo = 0; - switch (arrayMode) { - case Array::ForceExit: - ASSERT_NOT_REACHED(); - terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), NoNode); - return result; + switch (node.arrayMode()) { case Array::String: expectedClassInfo = &JSString::s_info; break; @@ -325,7 +328,9 @@ const TypedArrayDescriptor* SpeculativeJIT::speculateArray(Array::Mode arrayMode MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), Structure::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); - return result; + + noResult(m_compileIndex); + return; } case Array::Arguments: expectedClassInfo = &Arguments::s_info; @@ -356,7 +361,7 @@ const TypedArrayDescriptor* SpeculativeJIT::speculateArray(Array::Mode arrayMode MacroAssembler::Address(temp.gpr(), Structure::classInfoOffset()), MacroAssembler::TrustedImmPtr(expectedClassInfo))); - return result; + noResult(m_compileIndex); } GPRReg SpeculativeJIT::fillStorage(NodeIndex nodeIndex) @@ -393,8 +398,10 @@ GPRReg SpeculativeJIT::fillStorage(NodeIndex nodeIndex) void SpeculativeJIT::useChildren(Node& node) { if (node.flags() & NodeHasVarArgs) { - for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) - use(m_jit.graph().m_varArgChildren[childIdx]); + for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) { + if (!!m_jit.graph().m_varArgChildren[childIdx]) + use(m_jit.graph().m_varArgChildren[childIdx]); + } } else { Edge child1 = node.child1(); if (!child1) { @@ -1065,7 +1072,7 @@ void SpeculativeJIT::compilePeepHoleDoubleBranch(Node& node, NodeIndex branchNod jump(notTaken); } -void SpeculativeJIT::compilePeepHoleObjectEquality(Node& node, NodeIndex branchNodeIndex, const ClassInfo* classInfo, SpeculatedTypeChecker speculatedTypeChecker) +void SpeculativeJIT::compilePeepHoleObjectEquality(Node& node, NodeIndex branchNodeIndex) { Node& branchNode = at(branchNodeIndex); BlockIndex taken = branchNode.takenBlockIndex(); @@ -1086,11 +1093,47 @@ void SpeculativeJIT::compilePeepHoleObjectEquality(Node& node, NodeIndex branchN GPRReg op1GPR = op1.gpr(); GPRReg op2GPR = op2.gpr(); - if (!speculatedTypeChecker(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node.child1().index(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); - if (!speculatedTypeChecker(m_state.forNode(node.child2()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2().index(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op2GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); - + if (m_jit.graph().globalObjectFor(node.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(node.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node.child1().index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op1GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2().index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op2GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op1GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node.child1().index(), + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node.child1().index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); + + m_jit.loadPtr(MacroAssembler::Address(op2GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2().index(), + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2().index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); + } + branchPtr(condition, op1GPR, op2GPR, taken); jump(notTaken); } @@ -1144,37 +1187,17 @@ bool SpeculativeJIT::compilePeepHoleBranch(Node& node, MacroAssembler::Relationa else if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) compilePeepHoleDoubleBranch(node, branchNodeIndex, doubleCondition); else if (node.op() == CompareEq) { - if (Node::shouldSpeculateFinalObject( - at(node.child1()), at(node.child2()))) { - compilePeepHoleObjectEquality( - node, branchNodeIndex, &JSFinalObject::s_info, - isFinalObjectSpeculation); - } else if (Node::shouldSpeculateArray( - at(node.child1()), at(node.child2()))) { - compilePeepHoleObjectEquality( - node, branchNodeIndex, &JSArray::s_info, - isArraySpeculation); - } else if (at(node.child1()).shouldSpeculateFinalObject() - && at(node.child2()).shouldSpeculateFinalObjectOrOther()) { - compilePeepHoleObjectToObjectOrOtherEquality( - node.child1(), node.child2(), branchNodeIndex, - &JSFinalObject::s_info, isFinalObjectSpeculation); - } else if (at(node.child1()).shouldSpeculateFinalObjectOrOther() - && at(node.child2()).shouldSpeculateFinalObject()) { - compilePeepHoleObjectToObjectOrOtherEquality( - node.child2(), node.child1(), branchNodeIndex, - &JSFinalObject::s_info, isFinalObjectSpeculation); - } else if (at(node.child1()).shouldSpeculateArray() - && at(node.child2()).shouldSpeculateArrayOrOther()) { - compilePeepHoleObjectToObjectOrOtherEquality( - node.child1(), node.child2(), branchNodeIndex, - &JSArray::s_info, isArraySpeculation); - } else if (at(node.child1()).shouldSpeculateArrayOrOther() - && at(node.child2()).shouldSpeculateArray()) { - compilePeepHoleObjectToObjectOrOtherEquality( - node.child2(), node.child1(), branchNodeIndex, - &JSArray::s_info, isArraySpeculation); - } else { + if (at(node.child1()).shouldSpeculateString() || at(node.child2()).shouldSpeculateString()) { + nonSpeculativePeepholeBranch(node, branchNodeIndex, condition, operation); + return true; + } + if (at(node.child1()).shouldSpeculateNonStringCell() && at(node.child2()).shouldSpeculateNonStringCellOrOther()) + compilePeepHoleObjectToObjectOrOtherEquality(node.child1(), node.child2(), branchNodeIndex); + else if (at(node.child1()).shouldSpeculateNonStringCellOrOther() && at(node.child2()).shouldSpeculateNonStringCell()) + compilePeepHoleObjectToObjectOrOtherEquality(node.child2(), node.child1(), branchNodeIndex); + else if (at(node.child1()).shouldSpeculateNonStringCell() && at(node.child2()).shouldSpeculateNonStringCell()) + compilePeepHoleObjectEquality(node, branchNodeIndex); + else { nonSpeculativePeepholeBranch(node, branchNodeIndex, condition, operation); return true; } @@ -1461,51 +1484,6 @@ void SpeculativeJIT::checkArgumentTypes() m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); m_jit.xorPtr(TrustedImm32(static_cast<int32_t>(ValueFalse)), temp.gpr()); speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), TrustedImm32(static_cast<int32_t>(~1)))); - } else if (isInt8ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int8ArrayDescriptor().m_classInfo))); - } else if (isInt16ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int16ArrayDescriptor().m_classInfo))); - } else if (isInt32ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int32ArrayDescriptor().m_classInfo))); - } else if (isUint8ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint8ArrayDescriptor().m_classInfo))); - } else if (isUint8ClampedArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint8ClampedArrayDescriptor().m_classInfo))); - } else if (isUint16ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint16ArrayDescriptor().m_classInfo))); - } else if (isUint32ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint32ArrayDescriptor().m_classInfo))); - } else if (isFloat32ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float32ArrayDescriptor().m_classInfo))); - } else if (isFloat64ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister)); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float64ArrayDescriptor().m_classInfo))); } else if (isCellSpeculation(predictedType)) speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, JITCompiler::addressFor(virtualRegister), GPRInfo::tagMaskRegister)); #else @@ -1513,61 +1491,7 @@ void SpeculativeJIT::checkArgumentTypes() speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::Int32Tag))); else if (isBooleanSpeculation(predictedType)) speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::BooleanTag))); - else if (isInt8ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int8ArrayDescriptor().m_classInfo))); - } else if (isInt16ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int16ArrayDescriptor().m_classInfo))); - } else if (isInt32ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->int32ArrayDescriptor().m_classInfo))); - } else if (isUint8ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint8ArrayDescriptor().m_classInfo))); - } else if (isUint8ClampedArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint8ClampedArrayDescriptor().m_classInfo))); - } else if (isUint16ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint16ArrayDescriptor().m_classInfo))); - } else if (isUint32ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->uint32ArrayDescriptor().m_classInfo))); - } else if (isFloat32ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float32ArrayDescriptor().m_classInfo))); - } else if (isFloat64ArraySpeculation(predictedType)) { - GPRTemporary temp(this); - m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag))); - m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr()); - speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->float64ArrayDescriptor().m_classInfo))); - } else if (isCellSpeculation(predictedType)) + else if (isCellSpeculation(predictedType)) speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::CellTag))); #endif } @@ -2146,6 +2070,9 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& descriptor, GPRReg base, GPRReg property, Node& node, size_t elementSize, TypedArraySignedness signedness, TypedArrayRounding rounding) { + StorageOperand storage(this, m_jit.graph().varArgChild(node, 3)); + GPRReg storageReg = storage.gpr(); + Edge valueUse = m_jit.graph().varArgChild(node, 2); GPRTemporary value; @@ -2213,10 +2140,7 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(const TypedArrayDescriptor& } ASSERT_UNUSED(valueGPR, valueGPR != property); ASSERT(valueGPR != base); - GPRTemporary storage(this); - GPRReg storageReg = storage.gpr(); ASSERT(valueGPR != storageReg); - m_jit.loadPtr(MacroAssembler::Address(base, descriptor.m_storageOffset), storageReg); MacroAssembler::Jump outOfBounds; if (node.op() == PutByVal) outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, property, MacroAssembler::Address(base, descriptor.m_lengthOffset)); @@ -2278,6 +2202,9 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescriptor& descriptor, GPRReg base, GPRReg property, Node& node, size_t elementSize) { + StorageOperand storage(this, m_jit.graph().varArgChild(node, 3)); + GPRReg storageReg = storage.gpr(); + Edge baseUse = m_jit.graph().varArgChild(node, 0); Edge valueUse = m_jit.graph().varArgChild(node, 2); @@ -2287,10 +2214,6 @@ void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescripto GPRTemporary result(this); - GPRTemporary storage(this); - GPRReg storageReg = storage.gpr(); - - m_jit.loadPtr(MacroAssembler::Address(base, descriptor.m_storageOffset), storageReg); MacroAssembler::Jump outOfBounds; if (node.op() == PutByVal) outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, property, MacroAssembler::Address(base, descriptor.m_lengthOffset)); @@ -2892,45 +2815,23 @@ bool SpeculativeJIT::compare(Node& node, MacroAssembler::RelationalCondition con } if (node.op() == CompareEq) { - if (Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) { - compileObjectEquality(node, &JSFinalObject::s_info, isFinalObjectSpeculation); - return false; - } - - if (Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) { - compileObjectEquality(node, &JSArray::s_info, isArraySpeculation); - return false; - } - - if (at(node.child1()).shouldSpeculateFinalObject() - && at(node.child2()).shouldSpeculateFinalObjectOrOther()) { - compileObjectToObjectOrOtherEquality( - node.child1(), node.child2(), &JSFinalObject::s_info, - isFinalObjectSpeculation); + if (at(node.child1()).shouldSpeculateString() || at(node.child2()).shouldSpeculateString()) { + nonSpeculativeNonPeepholeCompare(node, condition, operation); return false; } - if (at(node.child1()).shouldSpeculateFinalObjectOrOther() - && at(node.child2()).shouldSpeculateFinalObject()) { - compileObjectToObjectOrOtherEquality( - node.child2(), node.child1(), &JSFinalObject::s_info, - isFinalObjectSpeculation); + if (at(node.child1()).shouldSpeculateNonStringCell() && at(node.child2()).shouldSpeculateNonStringCellOrOther()) { + compileObjectToObjectOrOtherEquality(node.child1(), node.child2()); return false; } - if (at(node.child1()).shouldSpeculateArray() - && at(node.child2()).shouldSpeculateArrayOrOther()) { - compileObjectToObjectOrOtherEquality( - node.child1(), node.child2(), &JSArray::s_info, - isArraySpeculation); + if (at(node.child1()).shouldSpeculateNonStringCellOrOther() && at(node.child2()).shouldSpeculateNonStringCell()) { + compileObjectToObjectOrOtherEquality(node.child2(), node.child1()); return false; } - - if (at(node.child1()).shouldSpeculateArrayOrOther() - && at(node.child2()).shouldSpeculateArray()) { - compileObjectToObjectOrOtherEquality( - node.child2(), node.child1(), &JSArray::s_info, - isArraySpeculation); + + if (at(node.child1()).shouldSpeculateNonStringCell() && at(node.child2()).shouldSpeculateNonStringCell()) { + compileObjectEquality(node); return false; } } @@ -3063,36 +2964,20 @@ bool SpeculativeJIT::compileStrictEq(Node& node) return false; } - // 4) If the operands are predicted final object or array, then do a final object - // or array comparison. - - if (Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) { + if (at(node.child1()).shouldSpeculateString() || at(node.child2()).shouldSpeculateString()) + return nonSpeculativeStrictEq(node); + if (at(node.child1()).shouldSpeculateNonStringCell() && at(node.child2()).shouldSpeculateNonStringCell()) { unsigned branchIndexInBlock = detectPeepHoleBranch(); if (branchIndexInBlock != UINT_MAX) { NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); - compilePeepHoleObjectEquality(node, branchNodeIndex, &JSFinalObject::s_info, isFinalObjectSpeculation); + compilePeepHoleObjectEquality(node, branchNodeIndex); use(node.child1()); use(node.child2()); m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; } - compileObjectEquality(node, &JSFinalObject::s_info, isFinalObjectSpeculation); - return false; - } - - if (Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) { - unsigned branchIndexInBlock = detectPeepHoleBranch(); - if (branchIndexInBlock != UINT_MAX) { - NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); - compilePeepHoleObjectEquality(node, branchNodeIndex, &JSArray::s_info, isArraySpeculation); - use(node.child1()); - use(node.child2()); - m_indexInBlock = branchIndexInBlock; - m_compileIndex = branchNodeIndex; - return true; - } - compileObjectEquality(node, &JSArray::s_info, isArraySpeculation); + compileObjectEquality(node); return false; } @@ -3109,8 +2994,7 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) GPRTemporary storage(this); GPRReg storageReg = storage.gpr(); - const TypedArrayDescriptor* descriptor = - speculateArray(node.arrayMode(), node.child1(), baseReg); + const TypedArrayDescriptor* descriptor = typedArrayDescriptor(node.arrayMode()); switch (node.arrayMode()) { case Array::String: @@ -3156,15 +3040,8 @@ void SpeculativeJIT::compileGetByValOnArguments(Node& node) if (!m_compileOkay) return; - - if (!isArgumentsSpeculation(m_state.forNode(node.child1()).m_type)) { - speculationCheck( - BadType, JSValueSource::unboxedCell(baseReg), node.child1(), - m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(&Arguments::s_info))); - } + + ASSERT(modeAlreadyChecked(m_state.forNode(node.child1()), Array::Arguments)); m_jit.loadPtr( MacroAssembler::Address(baseReg, Arguments::offsetOfData()), @@ -3225,14 +3102,7 @@ void SpeculativeJIT::compileGetArgumentsLength(Node& node) if (!m_compileOkay) return; - if (!isArgumentsSpeculation(m_state.forNode(node.child1()).m_type)) { - speculationCheck( - BadType, JSValueSource::unboxedCell(baseReg), node.child1(), - m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(&Arguments::s_info))); - } + ASSERT(modeAlreadyChecked(m_state.forNode(node.child1()), Array::Arguments)); m_jit.loadPtr( MacroAssembler::Address(baseReg, Arguments::offsetOfData()), @@ -3252,33 +3122,40 @@ void SpeculativeJIT::compileGetArgumentsLength(Node& node) void SpeculativeJIT::compileGetArrayLength(Node& node) { - SpeculateCellOperand base(this, node.child1()); - GPRTemporary result(this); - - GPRReg baseGPR = base.gpr(); - GPRReg resultGPR = result.gpr(); - - const TypedArrayDescriptor* descriptor = - speculateArray(node.arrayMode(), node.child1(), baseGPR); + const TypedArrayDescriptor* descriptor = typedArrayDescriptor(node.arrayMode()); switch (node.arrayMode()) { case Array::JSArray: - case Array::JSArrayOutOfBounds: - m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), resultGPR); - m_jit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), resultGPR); + case Array::JSArrayOutOfBounds: { + StorageOperand storage(this, node.child2()); + GPRTemporary result(this, storage); + GPRReg storageReg = storage.gpr(); + GPRReg resultReg = result.gpr(); + m_jit.load32(MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_length)), resultReg); - speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, resultGPR, MacroAssembler::TrustedImm32(0))); + speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::LessThan, resultReg, MacroAssembler::TrustedImm32(0))); - integerResult(resultGPR, m_compileIndex); + integerResult(resultReg, m_compileIndex); break; - case Array::String: + } + case Array::String: { + SpeculateCellOperand base(this, node.child1()); + GPRTemporary result(this, base); + GPRReg baseGPR = base.gpr(); + GPRReg resultGPR = result.gpr(); m_jit.load32(MacroAssembler::Address(baseGPR, JSString::offsetOfLength()), resultGPR); integerResult(resultGPR, m_compileIndex); break; - case Array::Arguments: + } + case Array::Arguments: { compileGetArgumentsLength(node); break; + } default: + SpeculateCellOperand base(this, node.child1()); + GPRTemporary result(this, base); + GPRReg baseGPR = base.gpr(); + GPRReg resultGPR = result.gpr(); ASSERT(descriptor); m_jit.load32(MacroAssembler::Address(baseGPR, descriptor->m_lengthOffset), resultGPR); integerResult(resultGPR, m_compileIndex); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 69a30a974..f7b125e1b 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1198,6 +1198,11 @@ public: m_jit.setupArgumentsWithExecState(arg1); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(J_DFGOperation_EAZ operation, GPRReg result, GPRReg arg1, GPRReg arg2) + { + m_jit.setupArgumentsWithExecState(arg1, arg2); + return appendCallWithExceptionCheckSetResult(operation, result); + } JITCompiler::Call callOperation(J_DFGOperation_ESt operation, GPRReg result, Structure* structure) { m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure)); @@ -1481,6 +1486,11 @@ public: m_jit.setupArgumentsWithExecState(arg1); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); } + JITCompiler::Call callOperation(J_DFGOperation_EAZ operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1, GPRReg arg2) + { + m_jit.setupArgumentsWithExecState(arg1, arg2); + return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); + } JITCompiler::Call callOperation(J_DFGOperation_ESt operation, GPRReg resultTag, GPRReg resultPayload, Structure* structure) { m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure)); @@ -1825,9 +1835,23 @@ public: return call; } #elif CPU(ARM) +#if CPU(ARM_HARDFP) JITCompiler::Call appendCallWithExceptionCheckSetResult(const FunctionPtr& function, FPRReg result) { JITCompiler::Call call = appendCallWithExceptionCheck(function); + m_jit.moveDouble(result, FPRInfo::argumentFPR0); + return call; + } + JITCompiler::Call appendCallSetResult(const FunctionPtr& function, FPRReg result) + { + JITCompiler::Call call = m_jit.appendCall(function); + m_jit.moveDouble(result, FPRInfo::argumentFPR0); + return call; + } +#else + JITCompiler::Call appendCallWithExceptionCheckSetResult(const FunctionPtr& function, FPRReg result) + { + JITCompiler::Call call = appendCallWithExceptionCheck(function); m_jit.assembler().vmov(result, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2); return call; } @@ -1837,6 +1861,7 @@ public: m_jit.assembler().vmov(result, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2); return call; } +#endif // CPU(ARM_HARDFP) #else JITCompiler::Call appendCallWithExceptionCheckSetResult(const FunctionPtr& function, FPRReg result) { @@ -2054,16 +2079,14 @@ public: bool compilePeepHoleBranch(Node&, MacroAssembler::RelationalCondition, MacroAssembler::DoubleCondition, S_DFGOperation_EJJ); void compilePeepHoleIntegerBranch(Node&, NodeIndex branchNodeIndex, JITCompiler::RelationalCondition); void compilePeepHoleDoubleBranch(Node&, NodeIndex branchNodeIndex, JITCompiler::DoubleCondition); - void compilePeepHoleObjectEquality(Node&, NodeIndex branchNodeIndex, const ClassInfo*, SpeculatedTypeChecker); - void compilePeepHoleObjectToObjectOrOtherEquality( - Edge leftChild, Edge rightChild, NodeIndex branchNodeIndex, const ClassInfo*, SpeculatedTypeChecker); - void compileObjectEquality(Node&, const ClassInfo*, SpeculatedTypeChecker); - void compileObjectToObjectOrOtherEquality( - Edge leftChild, Edge rightChild, const ClassInfo*, SpeculatedTypeChecker); + void compilePeepHoleObjectEquality(Node&, NodeIndex branchNodeIndex); + void compilePeepHoleObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild, NodeIndex branchNodeIndex); + void compileObjectEquality(Node&); + void compileObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild); void compileValueAdd(Node&); - void compileObjectOrOtherLogicalNot(Edge value, const ClassInfo*, bool needSpeculationCheck); + void compileNonStringCellOrOtherLogicalNot(Edge value, bool needSpeculationCheck); void compileLogicalNot(Node&); - void emitObjectOrOtherBranch(Edge value, BlockIndex taken, BlockIndex notTaken, const ClassInfo*, bool needSpeculationCheck); + void emitNonStringCellOrOtherBranch(Edge value, BlockIndex taken, BlockIndex notTaken, bool needSpeculationCheck); void emitBranch(Node&); void compileIntegerCompare(Node&, MacroAssembler::RelationalCondition); @@ -2197,7 +2220,7 @@ public: const TypedArrayDescriptor* typedArrayDescriptor(Array::Mode); - const TypedArrayDescriptor* speculateArray(Array::Mode, Edge baseEdge, GPRReg baseReg); + void checkArray(Node&); template<bool strict> GPRReg fillSpeculateIntInternal(NodeIndex, DataFormat& returnFormat); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 846d078ba..34b8dae46 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -1048,7 +1048,7 @@ void SpeculativeJIT::emitCall(Node& node) slowPath.append(m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleePayloadGPR, targetToCheck)); slowPath.append(m_jit.branch32(MacroAssembler::NotEqual, calleeTagGPR, TrustedImm32(JSValue::CellTag))); - m_jit.loadPtr(MacroAssembler::Address(calleePayloadGPR, OBJECT_OFFSETOF(JSFunction, m_scopeChain)), resultPayloadGPR); + m_jit.loadPtr(MacroAssembler::Address(calleePayloadGPR, OBJECT_OFFSETOF(JSFunction, m_scope)), resultPayloadGPR); m_jit.storePtr(resultPayloadGPR, MacroAssembler::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register)) * RegisterFile::ScopeChain + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); m_jit.store32(MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register)) * RegisterFile::ScopeChain + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); @@ -1489,17 +1489,53 @@ JITCompiler::Jump SpeculativeJIT::convertToDouble(JSValueOperand& op, FPRReg res return notNumber; } -void SpeculativeJIT::compileObjectEquality(Node& node, const ClassInfo* classInfo, SpeculatedTypeChecker speculatedTypeChecker) +void SpeculativeJIT::compileObjectEquality(Node& node) { SpeculateCellOperand op1(this, node.child1()); SpeculateCellOperand op2(this, node.child2()); GPRReg op1GPR = op1.gpr(); GPRReg op2GPR = op2.gpr(); - if (!speculatedTypeChecker(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); - if (!speculatedTypeChecker(m_state.forNode(node.child2()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op2GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); + if (m_jit.graph().globalObjectFor(node.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(node.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node.child1(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op1GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op2GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op1GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node.child1(), + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), node.child1(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); + + m_jit.loadPtr(MacroAssembler::Address(op2GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2(), + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueSource::unboxedCell(op2GPR), node.child2(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); + } GPRTemporary resultPayload(this, op2); GPRReg resultPayloadGPR = resultPayload.gpr(); @@ -1514,10 +1550,9 @@ void SpeculativeJIT::compileObjectEquality(Node& node, const ClassInfo* classInf booleanResult(resultPayloadGPR, m_compileIndex); } -void SpeculativeJIT::compileObjectToObjectOrOtherEquality( - Edge leftChild, Edge rightChild, - const ClassInfo* classInfo, SpeculatedTypeChecker speculatedTypeChecker) +void SpeculativeJIT::compileObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild) { + Node& leftNode = m_jit.graph()[leftChild.index()]; SpeculateCellOperand op1(this, leftChild); JSValueOperand op2(this, rightChild); GPRTemporary result(this); @@ -1527,31 +1562,59 @@ void SpeculativeJIT::compileObjectToObjectOrOtherEquality( GPRReg op2PayloadGPR = op2.payloadGPR(); GPRReg resultGPR = result.gpr(); - if (!speculatedTypeChecker(m_state.forNode(leftChild).m_type)) { - speculationCheck( - BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(), + if (m_jit.graph().globalObjectFor(leftNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(leftNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op1GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op1GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(), m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(classInfo))); + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); } + // It seems that most of the time when programs do a == b where b may be either null/undefined // or an object, b is usually an object. Balance the branches to make that case fast. MacroAssembler::Jump rightNotCell = m_jit.branch32(MacroAssembler::NotEqual, op2TagGPR, TrustedImm32(JSValue::CellTag)); - // We know that within this branch, rightChild must be a cell. If the CFA can tell us that the - // proof, when filtered on cell, demonstrates that we have an object of the desired type - // (speculationCheck() will test for FinalObject or Array, currently), then we can skip the - // speculation. - if (!speculatedTypeChecker(m_state.forNode(rightChild).m_type & SpecCell)) { - speculationCheck( - BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(), + // We know that within this branch, rightChild must be a cell. + if (m_jit.graph().globalObjectFor(leftNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(leftNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op2PayloadGPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op2PayloadGPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(), m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(op2PayloadGPR, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(classInfo))); + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); } // At this point we know that we can perform a straight-forward equality comparison on pointer @@ -1585,9 +1648,7 @@ void SpeculativeJIT::compileObjectToObjectOrOtherEquality( booleanResult(resultGPR, m_compileIndex); } -void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality( - Edge leftChild, Edge rightChild, NodeIndex branchNodeIndex, - const ClassInfo* classInfo, SpeculatedTypeChecker speculatedTypeChecker) +void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild, NodeIndex branchNodeIndex) { Node& branchNode = at(branchNodeIndex); BlockIndex taken = branchNode.takenBlockIndex(); @@ -1602,13 +1663,28 @@ void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality( GPRReg op2PayloadGPR = op2.payloadGPR(); GPRReg resultGPR = result.gpr(); - if (!speculatedTypeChecker(m_state.forNode(leftChild).m_type)) { - speculationCheck( - BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(), + if (m_jit.graph().globalObjectFor(branchNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(branchNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(), m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(classInfo))); + MacroAssembler::Equal, + MacroAssembler::Address(op1GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op1GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); } // It seems that most of the time when programs do a == b where b may be either null/undefined @@ -1616,17 +1692,29 @@ void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality( MacroAssembler::Jump rightNotCell = m_jit.branch32(MacroAssembler::NotEqual, op2TagGPR, TrustedImm32(JSValue::CellTag)); - // We know that within this branch, rightChild must be a cell. If the CFA can tell us that the - // proof, when filtered on cell, demonstrates that we have an object of the desired type - // (speculationCheck() will test for FinalObject or Array, currently), then we can skip the - // speculation. - if (!speculatedTypeChecker(m_state.forNode(rightChild).m_type & SpecCell)) { - speculationCheck( - BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(), + // We know that within this branch, rightChild must be a cell. + if (m_jit.graph().globalObjectFor(branchNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(branchNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op2PayloadGPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op2PayloadGPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(), m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(op2PayloadGPR, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(classInfo))); + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); } // At this point we know that we can perform a straight-forward equality comparison on pointer @@ -1703,7 +1791,7 @@ void SpeculativeJIT::compileValueAdd(Node& node) jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); } -void SpeculativeJIT::compileObjectOrOtherLogicalNot(Edge nodeUse, const ClassInfo* classInfo, bool needSpeculationCheck) +void SpeculativeJIT::compileNonStringCellOrOtherLogicalNot(Edge nodeUse, bool needSpeculationCheck) { JSValueOperand value(this, nodeUse); GPRTemporary resultPayload(this); @@ -1712,8 +1800,44 @@ void SpeculativeJIT::compileObjectOrOtherLogicalNot(Edge nodeUse, const ClassInf GPRReg resultPayloadGPR = resultPayload.gpr(); MacroAssembler::Jump notCell = m_jit.branch32(MacroAssembler::NotEqual, valueTagGPR, TrustedImm32(JSValue::CellTag)); - if (needSpeculationCheck) - speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(valuePayloadGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); + if (m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + + if (needSpeculationCheck) { + speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse, + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(valuePayloadGPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(valuePayloadGPR, JSCell::structureOffset()), structureGPR); + + if (needSpeculationCheck) { + speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse, + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } + + MacroAssembler::Jump isNotMasqueradesAsUndefined = + m_jit.branchTest8( + MacroAssembler::Zero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined)); + + speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse, + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(structureGPR, Structure::globalObjectOffset()), + MacroAssembler::TrustedImmPtr(m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)))); + + isNotMasqueradesAsUndefined.link(&m_jit); + } m_jit.move(TrustedImm32(0), resultPayloadGPR); MacroAssembler::Jump done = m_jit.jump(); @@ -1723,7 +1847,11 @@ void SpeculativeJIT::compileObjectOrOtherLogicalNot(Edge nodeUse, const ClassInf if (needSpeculationCheck) { m_jit.move(valueTagGPR, resultPayloadGPR); m_jit.or32(TrustedImm32(1), resultPayloadGPR); - speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse, m_jit.branch32(MacroAssembler::NotEqual, resultPayloadGPR, TrustedImm32(JSValue::NullTag))); + speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse, + m_jit.branch32( + MacroAssembler::NotEqual, + resultPayloadGPR, + TrustedImm32(JSValue::NullTag))); } m_jit.move(TrustedImm32(1), resultPayloadGPR); @@ -1741,12 +1869,9 @@ void SpeculativeJIT::compileLogicalNot(Node& node) booleanResult(result.gpr(), m_compileIndex); return; } - if (at(node.child1()).shouldSpeculateFinalObjectOrOther()) { - compileObjectOrOtherLogicalNot(node.child1(), &JSFinalObject::s_info, !isFinalObjectOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); - return; - } - if (at(node.child1()).shouldSpeculateArrayOrOther()) { - compileObjectOrOtherLogicalNot(node.child1(), &JSArray::s_info, !isArrayOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); + if (at(node.child1()).shouldSpeculateNonStringCellOrOther()) { + compileNonStringCellOrOtherLogicalNot(node.child1(), + !isNonStringCellOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); return; } if (at(node.child1()).shouldSpeculateInteger()) { @@ -1789,7 +1914,7 @@ void SpeculativeJIT::compileLogicalNot(Node& node) booleanResult(resultPayloadGPR, m_compileIndex, UseChildrenCalledExplicitly); } -void SpeculativeJIT::emitObjectOrOtherBranch(Edge nodeUse, BlockIndex taken, BlockIndex notTaken, const ClassInfo* classInfo, bool needSpeculationCheck) +void SpeculativeJIT::emitNonStringCellOrOtherBranch(Edge nodeUse, BlockIndex taken, BlockIndex notTaken, bool needSpeculationCheck) { JSValueOperand value(this, nodeUse); GPRTemporary scratch(this); @@ -1798,8 +1923,37 @@ void SpeculativeJIT::emitObjectOrOtherBranch(Edge nodeUse, BlockIndex taken, Blo GPRReg scratchGPR = scratch.gpr(); MacroAssembler::Jump notCell = m_jit.branch32(MacroAssembler::NotEqual, valueTagGPR, TrustedImm32(JSValue::CellTag)); - if (needSpeculationCheck) - speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(valuePayloadGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); + if (m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + + if (needSpeculationCheck) { + speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse, + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(valuePayloadGPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } + } else { + m_jit.loadPtr(MacroAssembler::Address(valuePayloadGPR, JSCell::structureOffset()), scratchGPR); + + if (needSpeculationCheck) { + speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + scratchGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } + + JITCompiler::Jump isNotMasqueradesAsUndefined = m_jit.branchTest8(JITCompiler::Zero, MacroAssembler::Address(scratchGPR, Structure::typeInfoFlagsOffset()), TrustedImm32(MasqueradesAsUndefined)); + + speculationCheck(BadType, JSValueRegs(valueTagGPR, valuePayloadGPR), nodeUse.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(scratchGPR, Structure::globalObjectOffset()), + MacroAssembler::TrustedImmPtr(m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)))); + + isNotMasqueradesAsUndefined.link(&m_jit); + } jump(taken, ForceJump); notCell.link(&m_jit); @@ -1836,10 +1990,9 @@ void SpeculativeJIT::emitBranch(Node& node) jump(notTaken); noResult(m_compileIndex); - } else if (at(node.child1()).shouldSpeculateFinalObjectOrOther()) { - emitObjectOrOtherBranch(node.child1(), taken, notTaken, &JSFinalObject::s_info, !isFinalObjectOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); - } else if (at(node.child1()).shouldSpeculateArrayOrOther()) { - emitObjectOrOtherBranch(node.child1(), taken, notTaken, &JSArray::s_info, !isArrayOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); + } else if (at(node.child1()).shouldSpeculateNonStringCellOrOther()) { + emitNonStringCellOrOtherBranch(node.child1(), taken, notTaken, + !isNonStringCellOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); } else if (at(node.child1()).shouldSpeculateNumber()) { if (at(node.child1()).shouldSpeculateInteger()) { bool invert = false; @@ -2373,6 +2526,11 @@ void SpeculativeJIT::compile(Node& node) compileGetByValOnString(node); break; } + + case CheckArray: { + checkArray(node); + break; + } case GetByVal: { switch (node.arrayMode()) { @@ -2396,8 +2554,7 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); break; } - case Array::JSArray: - case Array::JSArrayOutOfBounds: { + case Array::JSArray: { SpeculateStrictInt32Operand property(this, node.child2()); StorageOperand storage(this, node.child3()); GPRReg propertyReg = property.gpr(); @@ -2412,19 +2569,59 @@ void SpeculativeJIT::compile(Node& node) SpeculateCellOperand base(this, node.child1()); GPRReg baseReg = base.gpr(); // We've already speculated that it's some kind of array, at this point. - speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()))); + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()))); } GPRTemporary resultTag(this); GPRTemporary resultPayload(this); m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr()); - speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag))); + speculationCheck(OutOfBounds, 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)), resultPayload.gpr()); - + jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); break; } + case Array::JSArrayOutOfBounds: { + SpeculateCellOperand base(this, node.child1()); + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!m_compileOkay) + return; + + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + GPRReg resultTagReg = resultTag.gpr(); + GPRReg resultPayloadReg = resultPayload.gpr(); + + // 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. + GPRReg baseReg = base.gpr(); + // We've already speculated that it's some kind of array, at this point. + JITCompiler::Jump outOfBounds = m_jit.branch32( + MacroAssembler::AboveOrEqual, propertyReg, + MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())); + + m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTagReg); + JITCompiler::Jump hole = 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)), resultPayloadReg); + + JITCompiler::JumpList slowCases; + slowCases.append(outOfBounds); + slowCases.append(hole); + addSlowPathGenerator( + slowPathCall( + slowCases, this, operationGetByValArrayInt, + JSValueRegs(resultTagReg, resultPayloadReg), + baseReg, propertyReg)); + + jsValueResult(resultTagReg, resultPayloadReg, m_compileIndex); + break; + } case Array::String: compileGetByValOnString(node); break; @@ -2470,6 +2667,7 @@ void SpeculativeJIT::compile(Node& node) Edge child1 = m_jit.graph().varArgChild(node, 0); Edge child2 = m_jit.graph().varArgChild(node, 1); Edge child3 = m_jit.graph().varArgChild(node, 2); + Edge child4 = m_jit.graph().varArgChild(node, 3); Array::Mode arrayMode = modeForPut(node.arrayMode()); bool alreadyHandled = false; @@ -2513,8 +2711,6 @@ void SpeculativeJIT::compile(Node& node) GPRReg baseReg = base.gpr(); GPRReg propertyReg = property.gpr(); - speculateArray(arrayMode, child1, baseReg); - switch (arrayMode) { case Array::JSArray: case Array::JSArrayOutOfBounds: { @@ -2531,13 +2727,11 @@ void SpeculativeJIT::compile(Node& node) GPRReg scratchReg = scratch.gpr(); writeBarrier(baseReg, valueTagReg, child3, WriteBarrierForPropertyAccess, scratchReg); } + + StorageOperand storage(this, child4); + GPRReg storageReg = storage.gpr(); if (node.op() == PutByValAlias) { - // Get the array storage. - GPRTemporary storage(this); - GPRReg storageReg = storage.gpr(); - m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); - // Store the value to the array. GPRReg propertyReg = property.gpr(); m_jit.store32(value.tagGPR(), MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); @@ -2554,12 +2748,8 @@ void SpeculativeJIT::compile(Node& node) base.use(); property.use(); value.use(); + storage.use(); - // Get the array storage. - GPRTemporary storage(this); - GPRReg storageReg = storage.gpr(); - m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); - // Check if we're writing to a hole; if so increment m_numValuesInVector. MacroAssembler::Jump notHoleValue = m_jit.branch32(MacroAssembler::NotEqual, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), TrustedImm32(JSValue::EmptyValueTag)); m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); @@ -2688,6 +2878,8 @@ void SpeculativeJIT::compile(Node& node) } case ArrayPush: { + ASSERT(modeIsJSArray(node.arrayMode())); + SpeculateCellOperand base(this, node.child1()); JSValueOperand value(this, node.child2()); GPRTemporary storageLength(this); @@ -2697,17 +2889,14 @@ void SpeculativeJIT::compile(Node& node) GPRReg valuePayloadGPR = value.payloadGPR(); GPRReg storageLengthGPR = storageLength.gpr(); - { + if (Heap::isWriteBarrierEnabled()) { GPRTemporary scratch(this); writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); } - speculateArray(Array::JSArray, node.child1(), baseGPR); - - GPRTemporary storage(this); + StorageOperand storage(this, node.child3()); GPRReg storageGPR = storage.gpr(); - m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR); m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR); // Refuse to handle bizarre lengths. @@ -2730,10 +2919,12 @@ void SpeculativeJIT::compile(Node& node) } case ArrayPop: { + ASSERT(modeIsJSArray(node.arrayMode())); + SpeculateCellOperand base(this, node.child1()); + StorageOperand storage(this, node.child2()); GPRTemporary valueTag(this); GPRTemporary valuePayload(this); - GPRTemporary storage(this); GPRTemporary storageLength(this); GPRReg baseGPR = base.gpr(); @@ -2742,9 +2933,6 @@ void SpeculativeJIT::compile(Node& node) GPRReg storageGPR = storage.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); - speculateArray(Array::JSArray, node.child1(), baseGPR); - - m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR); m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR); JITCompiler::JumpList setUndefinedCases; @@ -3163,38 +3351,36 @@ void SpeculativeJIT::compile(Node& node) JITCompiler::Jump activationNotCreated; if (checkTopLevel) activationNotCreated = m_jit.branchTestPtr(JITCompiler::Zero, JITCompiler::addressFor(static_cast<VirtualRegister>(m_jit.codeBlock()->activationRegister()))); - m_jit.loadPtr(JITCompiler::Address(resultGPR, OBJECT_OFFSETOF(ScopeChainNode, next)), resultGPR); + m_jit.loadPtr(JITCompiler::Address(resultGPR, JSScope::offsetOfNext()), resultGPR); activationNotCreated.link(&m_jit); } while (skip--) - m_jit.loadPtr(JITCompiler::Address(resultGPR, OBJECT_OFFSETOF(ScopeChainNode, next)), resultGPR); + m_jit.loadPtr(JITCompiler::Address(resultGPR, JSScope::offsetOfNext()), resultGPR); - m_jit.loadPtr(JITCompiler::Address(resultGPR, OBJECT_OFFSETOF(ScopeChainNode, object)), resultGPR); - cellResult(resultGPR, m_compileIndex); break; } case GetScopedVar: { - SpeculateCellOperand scopeChain(this, node.child1()); + SpeculateCellOperand scope(this, node.child1()); GPRTemporary resultTag(this); GPRTemporary resultPayload(this); GPRReg resultTagGPR = resultTag.gpr(); GPRReg resultPayloadGPR = resultPayload.gpr(); - m_jit.loadPtr(JITCompiler::Address(scopeChain.gpr(), JSVariableObject::offsetOfRegisters()), resultPayloadGPR); + m_jit.loadPtr(JITCompiler::Address(scope.gpr(), JSVariableObject::offsetOfRegisters()), resultPayloadGPR); m_jit.load32(JITCompiler::Address(resultPayloadGPR, node.varNumber() * sizeof(Register) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); m_jit.load32(JITCompiler::Address(resultPayloadGPR, node.varNumber() * sizeof(Register) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultPayloadGPR); jsValueResult(resultTagGPR, resultPayloadGPR, m_compileIndex); break; } case PutScopedVar: { - SpeculateCellOperand scopeChain(this, node.child1()); + SpeculateCellOperand scope(this, node.child1()); GPRTemporary scratchRegister(this); GPRReg scratchGPR = scratchRegister.gpr(); - m_jit.loadPtr(JITCompiler::Address(scopeChain.gpr(), JSVariableObject::offsetOfRegisters()), scratchGPR); + m_jit.loadPtr(JITCompiler::Address(scope.gpr(), JSVariableObject::offsetOfRegisters()), scratchGPR); JSValueOperand value(this, node.child2()); m_jit.store32(value.tagGPR(), JITCompiler::Address(scratchGPR, node.varNumber() * sizeof(Register) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); m_jit.store32(value.payloadGPR(), JITCompiler::Address(scratchGPR, node.varNumber() * sizeof(Register) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); - writeBarrier(scopeChain.gpr(), value.tagGPR(), node.child2(), WriteBarrierForVariableAccess, scratchGPR); + writeBarrier(scope.gpr(), value.tagGPR(), node.child2(), WriteBarrierForVariableAccess, scratchGPR); noResult(m_compileIndex); break; } @@ -3535,7 +3721,7 @@ void SpeculativeJIT::compile(Node& node) JSValueOperand value(this, node.child1()); WatchpointSet* watchpointSet = - m_jit.globalObjectFor(node.codeOrigin)->symbolTable().get( + m_jit.globalObjectFor(node.codeOrigin)->symbolTable()->get( identifier(node.identifierNumberForCheck())->impl()).watchpointSet(); addSlowPathGenerator( slowPathCall( @@ -3563,7 +3749,7 @@ void SpeculativeJIT::compile(Node& node) } case GlobalVarWatchpoint: { - m_jit.globalObjectFor(node.codeOrigin)->symbolTable().get( + m_jit.globalObjectFor(node.codeOrigin)->symbolTable()->get( identifier(node.identifierNumberForCheck())->impl()).addWatchpoint( speculationWatchpoint()); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index b69d53600..a1ac899a2 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -1032,7 +1032,7 @@ void SpeculativeJIT::emitCall(Node& node) m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); slowPath = m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleeGPR, targetToCheck, MacroAssembler::TrustedImmPtr(JSValue::encode(JSValue()))); - m_jit.loadPtr(MacroAssembler::Address(calleeGPR, OBJECT_OFFSETOF(JSFunction, m_scopeChain)), resultGPR); + m_jit.loadPtr(MacroAssembler::Address(calleeGPR, OBJECT_OFFSETOF(JSFunction, m_scope)), resultGPR); m_jit.storePtr(resultGPR, MacroAssembler::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register)) * RegisterFile::ScopeChain)); CodeOrigin codeOrigin = at(m_compileIndex).codeOrigin; @@ -1538,7 +1538,7 @@ JITCompiler::Jump SpeculativeJIT::convertToDouble(GPRReg value, FPRReg result, G return notNumber; } -void SpeculativeJIT::compileObjectEquality(Node& node, const ClassInfo* classInfo, SpeculatedTypeChecker speculatedTypeChecker) +void SpeculativeJIT::compileObjectEquality(Node& node) { SpeculateCellOperand op1(this, node.child1()); SpeculateCellOperand op2(this, node.child2()); @@ -1547,11 +1547,47 @@ void SpeculativeJIT::compileObjectEquality(Node& node, const ClassInfo* classInf GPRReg op1GPR = op1.gpr(); GPRReg op2GPR = op2.gpr(); GPRReg resultGPR = result.gpr(); - - if (!speculatedTypeChecker(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueRegs(op1GPR), node.child1().index(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); - if (!speculatedTypeChecker(m_state.forNode(node.child2()).m_type)) - speculationCheck(BadType, JSValueRegs(op2GPR), node.child2().index(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op2GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); + + if (m_jit.graph().globalObjectFor(node.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(node.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueRegs(op1GPR), node.child1().index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op1GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueRegs(op2GPR), node.child2().index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op2GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op1GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueRegs(op1GPR), node.child1().index(), + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueRegs(op1GPR), node.child1().index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); + + m_jit.loadPtr(MacroAssembler::Address(op2GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueRegs(op2GPR), node.child2().index(), + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueRegs(op2GPR), node.child2().index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); + } MacroAssembler::Jump falseCase = m_jit.branchPtr(MacroAssembler::NotEqual, op1GPR, op2GPR); m_jit.move(TrustedImm32(ValueTrue), resultGPR); @@ -1563,10 +1599,9 @@ void SpeculativeJIT::compileObjectEquality(Node& node, const ClassInfo* classInf jsValueResult(resultGPR, m_compileIndex, DataFormatJSBoolean); } -void SpeculativeJIT::compileObjectToObjectOrOtherEquality( - Edge leftChild, Edge rightChild, - const ClassInfo* classInfo, SpeculatedTypeChecker speculatedTypeChecker) +void SpeculativeJIT::compileObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild) { + Node& leftNode = m_jit.graph()[leftChild.index()]; SpeculateCellOperand op1(this, leftChild); JSValueOperand op2(this, rightChild); GPRTemporary result(this); @@ -1574,14 +1609,29 @@ void SpeculativeJIT::compileObjectToObjectOrOtherEquality( GPRReg op1GPR = op1.gpr(); GPRReg op2GPR = op2.gpr(); GPRReg resultGPR = result.gpr(); - - if (!speculatedTypeChecker(m_state.forNode(leftChild).m_type)) { - speculationCheck( - BadType, JSValueRegs(op1GPR), leftChild.index(), + + if (m_jit.graph().globalObjectFor(leftNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(leftNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueRegs(op1GPR), leftChild.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op1GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op1GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueRegs(op1GPR), leftChild.index(), m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(classInfo))); + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueRegs(op1GPR), leftChild.index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); } // It seems that most of the time when programs do a == b where b may be either null/undefined @@ -1589,17 +1639,29 @@ void SpeculativeJIT::compileObjectToObjectOrOtherEquality( MacroAssembler::Jump rightNotCell = m_jit.branchTestPtr(MacroAssembler::NonZero, op2GPR, GPRInfo::tagMaskRegister); - // We know that within this branch, rightChild must be a cell. If the CFA can tell us that the - // proof, when filtered on cell, demonstrates that we have an object of the desired type - // (speculationCheck() will test for FinalObject or Array, currently), then we can skip the - // speculation. - if (!speculatedTypeChecker(m_state.forNode(rightChild).m_type & SpecCell)) { - speculationCheck( - BadType, JSValueRegs(op2GPR), rightChild.index(), + // We know that within this branch, rightChild must be a cell. + if (m_jit.graph().globalObjectFor(leftNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(leftNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueRegs(op2GPR), rightChild.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(op2GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op2GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueRegs(op2GPR), rightChild.index(), m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(op2GPR, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(classInfo))); + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueRegs(op2GPR), rightChild.index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); } // At this point we know that we can perform a straight-forward equality comparison on pointer @@ -1633,9 +1695,7 @@ void SpeculativeJIT::compileObjectToObjectOrOtherEquality( jsValueResult(resultGPR, m_compileIndex, DataFormatJSBoolean); } -void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality( - Edge leftChild, Edge rightChild, NodeIndex branchNodeIndex, - const ClassInfo* classInfo, SpeculatedTypeChecker speculatedTypeChecker) +void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild, NodeIndex branchNodeIndex) { Node& branchNode = at(branchNodeIndex); BlockIndex taken = branchNode.takenBlockIndex(); @@ -1649,13 +1709,28 @@ void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality( GPRReg op2GPR = op2.gpr(); GPRReg resultGPR = result.gpr(); - if (!speculatedTypeChecker(m_state.forNode(leftChild).m_type)) { - speculationCheck( - BadType, JSValueRegs(op1GPR), leftChild.index(), + if (m_jit.graph().globalObjectFor(branchNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(branchNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueRegs(op1GPR), leftChild.index(), m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(classInfo))); + MacroAssembler::Equal, + MacroAssembler::Address(op1GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op1GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueRegs(op1GPR), leftChild.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueRegs(op1GPR), leftChild.index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); } // It seems that most of the time when programs do a == b where b may be either null/undefined @@ -1663,17 +1738,29 @@ void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality( MacroAssembler::Jump rightNotCell = m_jit.branchTestPtr(MacroAssembler::NonZero, op2GPR, GPRInfo::tagMaskRegister); - // We know that within this branch, rightChild must be a cell. If the CFA can tell us that the - // proof, when filtered on cell, demonstrates that we have an object of the desired type - // (speculationCheck() will test for FinalObject or Array, currently), then we can skip the - // speculation. - if (!speculatedTypeChecker(m_state.forNode(rightChild).m_type & SpecCell)) { - speculationCheck( - BadType, JSValueRegs(op2GPR), rightChild.index(), + // We know that within this branch, rightChild must be a cell. + if (m_jit.graph().globalObjectFor(branchNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(branchNode.codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + speculationCheck(BadType, JSValueRegs(op2GPR), rightChild.index(), m_jit.branchPtr( - MacroAssembler::NotEqual, - MacroAssembler::Address(op2GPR, JSCell::classInfoOffset()), - MacroAssembler::TrustedImmPtr(classInfo))); + MacroAssembler::Equal, + MacroAssembler::Address(op2GPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(op2GPR, JSCell::structureOffset()), structureGPR); + speculationCheck(BadType, JSValueRegs(op2GPR), rightChild.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + speculationCheck(BadType, JSValueRegs(op2GPR), rightChild.index(), + m_jit.branchTest8( + MacroAssembler::NonZero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined))); } // At this point we know that we can perform a straight-forward equality comparison on pointer @@ -1748,7 +1835,7 @@ void SpeculativeJIT::compileValueAdd(Node& node) jsValueResult(result.gpr(), m_compileIndex); } -void SpeculativeJIT::compileObjectOrOtherLogicalNot(Edge nodeUse, const ClassInfo* classInfo, bool needSpeculationCheck) +void SpeculativeJIT::compileNonStringCellOrOtherLogicalNot(Edge nodeUse, bool needSpeculationCheck) { JSValueOperand value(this, nodeUse); GPRTemporary result(this); @@ -1756,19 +1843,59 @@ void SpeculativeJIT::compileObjectOrOtherLogicalNot(Edge nodeUse, const ClassInf GPRReg resultGPR = result.gpr(); MacroAssembler::Jump notCell = m_jit.branchTestPtr(MacroAssembler::NonZero, valueGPR, GPRInfo::tagMaskRegister); - if (needSpeculationCheck) - speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(valueGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); - m_jit.move(TrustedImm32(static_cast<int32_t>(ValueFalse)), resultGPR); + if (m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + + if (needSpeculationCheck) { + speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse, + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(valueGPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } + } else { + GPRTemporary structure(this); + GPRReg structureGPR = structure.gpr(); + + m_jit.loadPtr(MacroAssembler::Address(valueGPR, JSCell::structureOffset()), structureGPR); + + if (needSpeculationCheck) { + speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse, + m_jit.branchPtr( + MacroAssembler::Equal, + structureGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } + + MacroAssembler::Jump isNotMasqueradesAsUndefined = + m_jit.branchTest8( + MacroAssembler::Zero, + MacroAssembler::Address(structureGPR, Structure::typeInfoFlagsOffset()), + MacroAssembler::TrustedImm32(MasqueradesAsUndefined)); + + speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse, + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(structureGPR, Structure::globalObjectOffset()), + MacroAssembler::TrustedImmPtr(m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)))); + + isNotMasqueradesAsUndefined.link(&m_jit); + } + m_jit.move(TrustedImm32(ValueFalse), resultGPR); MacroAssembler::Jump done = m_jit.jump(); notCell.link(&m_jit); - + if (needSpeculationCheck) { m_jit.move(valueGPR, resultGPR); m_jit.andPtr(MacroAssembler::TrustedImm32(~TagBitUndefined), resultGPR); - speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse, m_jit.branchPtr(MacroAssembler::NotEqual, resultGPR, MacroAssembler::TrustedImmPtr(reinterpret_cast<void*>(ValueNull)))); + speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse, + m_jit.branchPtr( + MacroAssembler::NotEqual, + resultGPR, + MacroAssembler::TrustedImmPtr(reinterpret_cast<void*>(ValueNull)))); } - m_jit.move(TrustedImm32(static_cast<int32_t>(ValueTrue)), resultGPR); + m_jit.move(TrustedImm32(ValueTrue), resultGPR); done.link(&m_jit); @@ -1777,12 +1904,9 @@ void SpeculativeJIT::compileObjectOrOtherLogicalNot(Edge nodeUse, const ClassInf void SpeculativeJIT::compileLogicalNot(Node& node) { - if (at(node.child1()).shouldSpeculateFinalObjectOrOther()) { - compileObjectOrOtherLogicalNot(node.child1(), &JSFinalObject::s_info, !isFinalObjectOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); - return; - } - if (at(node.child1()).shouldSpeculateArrayOrOther()) { - compileObjectOrOtherLogicalNot(node.child1(), &JSArray::s_info, !isArrayOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); + if (at(node.child1()).shouldSpeculateNonStringCellOrOther()) { + compileNonStringCellOrOtherLogicalNot(node.child1(), + !isNonStringCellOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); return; } if (at(node.child1()).shouldSpeculateInteger()) { @@ -1850,7 +1974,7 @@ void SpeculativeJIT::compileLogicalNot(Node& node) jsValueResult(resultGPR, m_compileIndex, DataFormatJSBoolean, UseChildrenCalledExplicitly); } -void SpeculativeJIT::emitObjectOrOtherBranch(Edge nodeUse, BlockIndex taken, BlockIndex notTaken, const ClassInfo* classInfo, bool needSpeculationCheck) +void SpeculativeJIT::emitNonStringCellOrOtherBranch(Edge nodeUse, BlockIndex taken, BlockIndex notTaken, bool needSpeculationCheck) { JSValueOperand value(this, nodeUse); GPRTemporary scratch(this); @@ -1858,8 +1982,37 @@ void SpeculativeJIT::emitObjectOrOtherBranch(Edge nodeUse, BlockIndex taken, Blo GPRReg scratchGPR = scratch.gpr(); MacroAssembler::Jump notCell = m_jit.branchTestPtr(MacroAssembler::NonZero, valueGPR, GPRInfo::tagMaskRegister); - if (needSpeculationCheck) - speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse.index(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(valueGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); + if (m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) { + m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)->masqueradesAsUndefinedWatchpoint()->add(speculationWatchpoint()); + + if (needSpeculationCheck) { + speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(valueGPR, JSCell::structureOffset()), + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } + } else { + m_jit.loadPtr(MacroAssembler::Address(valueGPR, JSCell::structureOffset()), scratchGPR); + + if (needSpeculationCheck) { + speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + scratchGPR, + MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); + } + + JITCompiler::Jump isNotMasqueradesAsUndefined = m_jit.branchTest8(JITCompiler::Zero, MacroAssembler::Address(scratchGPR, Structure::typeInfoFlagsOffset()), TrustedImm32(MasqueradesAsUndefined)); + + speculationCheck(BadType, JSValueRegs(valueGPR), nodeUse.index(), + m_jit.branchPtr( + MacroAssembler::Equal, + MacroAssembler::Address(scratchGPR, Structure::globalObjectOffset()), + MacroAssembler::TrustedImmPtr(m_jit.graph().globalObjectFor(m_jit.graph()[nodeUse.index()].codeOrigin)))); + + isNotMasqueradesAsUndefined.link(&m_jit); + } jump(taken, ForceJump); notCell.link(&m_jit); @@ -1879,10 +2032,9 @@ void SpeculativeJIT::emitBranch(Node& node) BlockIndex taken = node.takenBlockIndex(); BlockIndex notTaken = node.notTakenBlockIndex(); - if (at(node.child1()).shouldSpeculateFinalObjectOrOther()) { - emitObjectOrOtherBranch(node.child1(), taken, notTaken, &JSFinalObject::s_info, !isFinalObjectOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); - } else if (at(node.child1()).shouldSpeculateArrayOrOther()) { - emitObjectOrOtherBranch(node.child1(), taken, notTaken, &JSArray::s_info, !isArrayOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); + if (at(node.child1()).shouldSpeculateNonStringCellOrOther()) { + emitNonStringCellOrOtherBranch(node.child1(), taken, notTaken, + !isNonStringCellOrOtherSpeculation(m_state.forNode(node.child1()).m_type)); } else if (at(node.child1()).shouldSpeculateNumber()) { if (at(node.child1()).shouldSpeculateInteger()) { bool invert = false; @@ -2406,6 +2558,11 @@ void SpeculativeJIT::compile(Node& node) compileGetByValOnString(node); break; } + + case CheckArray: { + checkArray(node); + break; + } case GetByVal: { switch (node.arrayMode()) { @@ -2443,11 +2600,24 @@ void SpeculativeJIT::compile(Node& node) // We will have already speculated that the base is some kind of array, // at this point. - speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()))); + MacroAssembler::Jump outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())); + if (node.arrayMode() == Array::JSArray) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, outOfBounds); GPRTemporary result(this); m_jit.loadPtr(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), result.gpr()); - speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr())); + MacroAssembler::Jump hole = m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()); + if (node.arrayMode() == Array::JSArray) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, hole); + else { + MacroAssembler::JumpList slowCases; + slowCases.append(outOfBounds); + slowCases.append(hole); + addSlowPathGenerator( + slowPathCall( + slowCases, this, operationGetByValArrayInt, + result.gpr(), baseReg, propertyReg)); + } jsValueResult(result.gpr(), m_compileIndex); break; @@ -2497,6 +2667,7 @@ void SpeculativeJIT::compile(Node& node) Edge child1 = m_jit.graph().varArgChild(node, 0); Edge child2 = m_jit.graph().varArgChild(node, 1); Edge child3 = m_jit.graph().varArgChild(node, 2); + Edge child4 = m_jit.graph().varArgChild(node, 3); Array::Mode arrayMode = modeForPut(node.arrayMode()); bool alreadyHandled = false; @@ -2538,27 +2709,26 @@ void SpeculativeJIT::compile(Node& node) GPRReg baseReg = base.gpr(); GPRReg propertyReg = property.gpr(); - speculateArray(arrayMode, child1, baseReg); - switch (arrayMode) { case Array::JSArray: case Array::JSArrayOutOfBounds: { JSValueOperand value(this, child3); - GPRTemporary scratch(this); // Map base, property & value into registers, allocate a scratch register. GPRReg valueReg = value.gpr(); - GPRReg scratchReg = scratch.gpr(); if (!m_compileOkay) return; - writeBarrier(baseReg, value.gpr(), child3, WriteBarrierForPropertyAccess, scratchReg); + if (Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseReg, value.gpr(), child3, WriteBarrierForPropertyAccess, scratch.gpr()); + } + + StorageOperand storage(this, child4); + GPRReg storageReg = storage.gpr(); if (node.op() == PutByValAlias) { - GPRReg storageReg = scratchReg; - m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); - // Store the value to the array. GPRReg propertyReg = property.gpr(); GPRReg valueReg = value.gpr(); @@ -2575,11 +2745,8 @@ void SpeculativeJIT::compile(Node& node) base.use(); property.use(); value.use(); + storage.use(); - // Get the array storage. - GPRReg storageReg = scratchReg; - m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); - // Check if we're writing to a hole; if so increment m_numValuesInVector. MacroAssembler::Jump notHoleValue = m_jit.branchTestPtr(MacroAssembler::NonZero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); @@ -2747,21 +2914,24 @@ void SpeculativeJIT::compile(Node& node) } case ArrayPush: { + ASSERT(modeIsJSArray(node.arrayMode())); + SpeculateCellOperand base(this, node.child1()); JSValueOperand value(this, node.child2()); - GPRTemporary storage(this); GPRTemporary storageLength(this); GPRReg baseGPR = base.gpr(); GPRReg valueGPR = value.gpr(); - GPRReg storageGPR = storage.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); - writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, storageGPR, storageLengthGPR); + if (Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } - speculateArray(Array::JSArray, node.child1(), baseGPR); + StorageOperand storage(this, node.child3()); + GPRReg storageGPR = storage.gpr(); - m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR); m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR); // Refuse to handle bizarre lengths. @@ -2786,19 +2956,18 @@ void SpeculativeJIT::compile(Node& node) } case ArrayPop: { + ASSERT(modeIsJSArray(node.arrayMode())); + SpeculateCellOperand base(this, node.child1()); + StorageOperand storage(this, node.child2()); GPRTemporary value(this); - GPRTemporary storage(this); GPRTemporary storageLength(this); GPRReg baseGPR = base.gpr(); - GPRReg valueGPR = value.gpr(); GPRReg storageGPR = storage.gpr(); + GPRReg valueGPR = value.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); - speculateArray(Array::JSArray, node.child1(), baseGPR); - - m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR); m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR); JITCompiler::JumpList setUndefinedCases; @@ -3175,34 +3344,32 @@ void SpeculativeJIT::compile(Node& node) JITCompiler::Jump activationNotCreated; if (checkTopLevel) activationNotCreated = m_jit.branchTestPtr(JITCompiler::Zero, JITCompiler::addressFor(static_cast<VirtualRegister>(m_jit.codeBlock()->activationRegister()))); - m_jit.loadPtr(JITCompiler::Address(resultGPR, OBJECT_OFFSETOF(ScopeChainNode, next)), resultGPR); + m_jit.loadPtr(JITCompiler::Address(resultGPR, JSScope::offsetOfNext()), resultGPR); activationNotCreated.link(&m_jit); } while (skip--) - m_jit.loadPtr(JITCompiler::Address(resultGPR, OBJECT_OFFSETOF(ScopeChainNode, next)), resultGPR); + m_jit.loadPtr(JITCompiler::Address(resultGPR, JSScope::offsetOfNext()), resultGPR); - m_jit.loadPtr(JITCompiler::Address(resultGPR, OBJECT_OFFSETOF(ScopeChainNode, object)), resultGPR); - cellResult(resultGPR, m_compileIndex); break; } case GetScopedVar: { - SpeculateCellOperand scopeChain(this, node.child1()); + SpeculateCellOperand scope(this, node.child1()); GPRTemporary result(this); GPRReg resultGPR = result.gpr(); - m_jit.loadPtr(JITCompiler::Address(scopeChain.gpr(), JSVariableObject::offsetOfRegisters()), resultGPR); + m_jit.loadPtr(JITCompiler::Address(scope.gpr(), JSVariableObject::offsetOfRegisters()), resultGPR); m_jit.loadPtr(JITCompiler::Address(resultGPR, node.varNumber() * sizeof(Register)), resultGPR); jsValueResult(resultGPR, m_compileIndex); break; } case PutScopedVar: { - SpeculateCellOperand scopeChain(this, node.child1()); + SpeculateCellOperand scope(this, node.child1()); GPRTemporary scratchRegister(this); GPRReg scratchGPR = scratchRegister.gpr(); - m_jit.loadPtr(JITCompiler::Address(scopeChain.gpr(), JSVariableObject::offsetOfRegisters()), scratchGPR); + m_jit.loadPtr(JITCompiler::Address(scope.gpr(), JSVariableObject::offsetOfRegisters()), scratchGPR); JSValueOperand value(this, node.child2()); m_jit.storePtr(value.gpr(), JITCompiler::Address(scratchGPR, node.varNumber() * sizeof(Register))); - writeBarrier(scopeChain.gpr(), value.gpr(), node.child2(), WriteBarrierForVariableAccess, scratchGPR); + writeBarrier(scope.gpr(), value.gpr(), node.child2(), WriteBarrierForVariableAccess, scratchGPR); noResult(m_compileIndex); break; } @@ -3518,7 +3685,7 @@ void SpeculativeJIT::compile(Node& node) JSValueOperand value(this, node.child1()); WatchpointSet* watchpointSet = - m_jit.globalObjectFor(node.codeOrigin)->symbolTable().get( + m_jit.globalObjectFor(node.codeOrigin)->symbolTable()->get( identifier(node.identifierNumberForCheck())->impl()).watchpointSet(); addSlowPathGenerator( slowPathCall( @@ -3541,7 +3708,7 @@ void SpeculativeJIT::compile(Node& node) } case GlobalVarWatchpoint: { - m_jit.globalObjectFor(node.codeOrigin)->symbolTable().get( + m_jit.globalObjectFor(node.codeOrigin)->symbolTable()->get( identifier(node.identifierNumberForCheck())->impl()).addWatchpoint( speculationWatchpoint()); diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp index eb04a6747..dec2e49f7 100644 --- a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp @@ -99,6 +99,8 @@ public: case PutByVal: case PutByValAlias: case GetArrayLength: + case CheckArray: + case GetIndexedPropertyStorage: case Phantom: // Don't count these uses. break; @@ -128,6 +130,50 @@ public: #endif iter->second.m_structure = 0; } + + // Disable structure check hoisting for variables that cross the OSR entry that + // we're currently taking, and where the value currently does not have the + // structure we want. + + for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { + BasicBlock* block = m_graph.m_blocks[blockIndex].get(); + if (!block) + continue; + ASSERT(block->isReachable); + if (!block->isOSRTarget) + continue; + if (block->bytecodeBegin != m_graph.m_osrEntryBytecodeIndex) + continue; + for (size_t i = 0; i < m_graph.m_mustHandleValues.size(); ++i) { + int operand = m_graph.m_mustHandleValues.operandForIndex(i); + NodeIndex nodeIndex = block->variablesAtHead.operand(operand); + if (nodeIndex == NoNode) + continue; + VariableAccessData* variable = m_graph[nodeIndex].variableAccessData(); + HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); + if (iter == m_map.end()) + continue; + if (!iter->second.m_structure) + continue; + JSValue value = m_graph.m_mustHandleValues[i]; + if (!value || !value.isCell()) { +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + dataLog("Zeroing the structure to hoist for %s because the OSR entry value is not a cell: %s.\n", + m_graph.nameOfVariableAccessData(variable), value.description()); +#endif + iter->second.m_structure = 0; + continue; + } + if (value.asCell()->structure() != iter->second.m_structure) { +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + dataLog("Zeroing the structure to hoist for %s because the OSR entry value has structure %p and we wanted %p.\n", + m_graph.nameOfVariableAccessData(variable), value.asCell()->structure(), iter->second.m_structure); +#endif + iter->second.m_structure = 0; + continue; + } + } + } // Identify the set of variables that are live across a structure clobber. diff --git a/Source/JavaScriptCore/dfg/DFGThunks.cpp b/Source/JavaScriptCore/dfg/DFGThunks.cpp index b056de79c..546aec256 100644 --- a/Source/JavaScriptCore/dfg/DFGThunks.cpp +++ b/Source/JavaScriptCore/dfg/DFGThunks.cpp @@ -219,10 +219,11 @@ static MacroAssemblerCodeRef virtualForThunkGenerator( CCallHelpers::NotEqual, GPRInfo::nonArgGPR1, CCallHelpers::TrustedImm32(JSValue::CellTag))); #endif + jit.loadPtr(CCallHelpers::Address(GPRInfo::nonArgGPR0, JSCell::structureOffset()), GPRInfo::nonArgGPR2); slowCase.append( jit.branchPtr( CCallHelpers::NotEqual, - CCallHelpers::Address(GPRInfo::nonArgGPR0, JSCell::classInfoOffset()), + CCallHelpers::Address(GPRInfo::nonArgGPR2, Structure::classInfoOffset()), CCallHelpers::TrustedImmPtr(&JSFunction::s_info))); // Now we know we have a JSFunction. |