diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-08-12 09:27:39 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-08-12 09:27:39 +0200 |
commit | 3749d61e1f7a59f5ec5067e560af1eb610c82015 (patch) | |
tree | 73dc228333948738bbe02976cacca8cd382bc978 /Source/JavaScriptCore/dfg | |
parent | b32b4dcd9a51ab8de6afc53d9e17f8707e1f7a5e (diff) | |
download | qtwebkit-3749d61e1f7a59f5ec5067e560af1eb610c82015.tar.gz |
Imported WebKit commit a77350243e054f3460d1137301d8b3faee3d2052 (http://svn.webkit.org/repository/webkit/trunk@125365)
New snapshot with build fixes for latest API changes in Qt and all WK1 Win MSVC fixes upstream
Diffstat (limited to 'Source/JavaScriptCore/dfg')
22 files changed, 1110 insertions, 298 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index 01996f132..4f02ee793 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -950,7 +950,8 @@ bool AbstractState::execute(unsigned indexInBlock) } case PutByVal: - case PutByValAlias: { + case PutByValAlias: + case PutByValSafe: { node.setCanExit(true); Edge child1 = m_graph.varArgChild(node, 0); @@ -966,7 +967,7 @@ bool AbstractState::execute(unsigned indexInBlock) || m_graph[child1].shouldSpeculateArguments() #endif ) { - ASSERT(node.op() == PutByVal); + ASSERT(node.op() == PutByVal || node.op() == PutByValSafe); clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; @@ -1055,7 +1056,7 @@ bool AbstractState::execute(unsigned indexInBlock) ASSERT(m_graph[child1].shouldSpeculateArray()); forNode(child1).filter(SpecArray); forNode(child2).filter(SpecInt32); - if (node.op() == PutByVal) + if (node.op() == PutByValSafe) clobberWorld(node.codeOrigin, indexInBlock); break; } @@ -1335,7 +1336,7 @@ bool AbstractState::execute(unsigned indexInBlock) case PutScopedVar: node.setCanExit(false); - clobberStructures(indexInBlock); + clobberCapturedVars(node.codeOrigin); break; case GetById: @@ -1415,7 +1416,8 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).set(SpecInt32); break; - case CheckStructure: { + case CheckStructure: + case ForwardCheckStructure: { // FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes). AbstractValue& value = forNode(node.child1()); node.setCanExit( @@ -1431,6 +1433,7 @@ bool AbstractState::execute(unsigned indexInBlock) AbstractValue& value = forNode(node.child1()); ASSERT(value.isClear() || isCellSpeculation(value.m_type)); // Value could be clear if we've proven must-exit due to a speculation statically known to be bad. value.filter(node.structure()); + m_haveStructures = true; node.setCanExit(true); break; } @@ -1609,6 +1612,12 @@ bool AbstractState::execute(unsigned indexInBlock) inline void AbstractState::clobberWorld(const CodeOrigin& codeOrigin, unsigned indexInBlock) { + clobberCapturedVars(codeOrigin); + clobberStructures(indexInBlock); +} + +inline void AbstractState::clobberCapturedVars(const CodeOrigin& codeOrigin) +{ if (codeOrigin.inlineCallFrame) { const BitVector& capturedVars = codeOrigin.inlineCallFrame->capturedVars; for (size_t i = capturedVars.size(); i--;) { @@ -1624,7 +1633,6 @@ inline void AbstractState::clobberWorld(const CodeOrigin& codeOrigin, unsigned i for (size_t i = m_variables.numberOfArguments(); i--;) m_variables.argument(i).makeTop(); } - clobberStructures(indexInBlock); } inline void AbstractState::clobberStructures(unsigned indexInBlock) diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.h b/Source/JavaScriptCore/dfg/DFGAbstractState.h index 95cadecbb..d2bc1a551 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.h @@ -217,6 +217,7 @@ public: private: void clobberWorld(const CodeOrigin&, unsigned indexInBlock); + void clobberCapturedVars(const CodeOrigin&); void clobberStructures(unsigned indexInBlock); bool mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, NodeIndex); diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h index f81af4ecf..402fd0fcd 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h @@ -317,13 +317,14 @@ struct AbstractValue { { m_type = SpecNone; m_structure.clear(); + m_unclobberedStructure.clear(); m_value = JSValue(); checkConsistency(); } bool isClear() const { - bool result = m_type == SpecNone && m_structure.isClear(); + bool result = m_type == SpecNone && m_structure.isClear() && m_unclobberedStructure.isClear(); if (result) ASSERT(!m_value); return result; @@ -333,6 +334,7 @@ struct AbstractValue { { m_type = SpecTop; m_structure.makeTop(); + m_unclobberedStructure.makeTop(); m_value = JSValue(); checkConsistency(); } @@ -353,7 +355,7 @@ struct AbstractValue { bool isTop() const { - return m_type == SpecTop && m_structure.isTop(); + return m_type == SpecTop && m_structure.isTop() && m_unclobberedStructure.isTop(); } bool valueIsTop() const @@ -383,8 +385,11 @@ struct AbstractValue { // it may change in the future - for example between when we compile // the code and when we run it. m_structure.makeTop(); - } else + m_unclobberedStructure.makeTop(); // FIXME: Consider not clobbering this. + } else { m_structure.clear(); + m_unclobberedStructure.clear(); + } m_type = speculationFromValue(value); m_value = value; @@ -397,6 +402,9 @@ struct AbstractValue { m_structure.clear(); m_structure.add(structure); + m_unclobberedStructure.clear(); + m_unclobberedStructure.add(structure); + m_type = speculationFromStructure(structure); m_value = JSValue(); @@ -405,10 +413,13 @@ struct AbstractValue { void set(SpeculatedType type) { - if (type & SpecCell) + if (type & SpecCell) { m_structure.makeTop(); - else + m_unclobberedStructure.makeTop(); + } else { m_structure.clear(); + m_unclobberedStructure.clear(); + } m_type = type; m_value = JSValue(); checkConsistency(); @@ -418,6 +429,7 @@ struct AbstractValue { { return m_type == other.m_type && m_structure == other.m_structure + && m_unclobberedStructure == other.m_unclobberedStructure && m_value == other.m_value; } bool operator!=(const AbstractValue& other) const @@ -437,6 +449,7 @@ struct AbstractValue { } else { result |= mergeSpeculation(m_type, other.m_type); result |= m_structure.addAll(other.m_structure); + result |= m_unclobberedStructure.addAll(other.m_unclobberedStructure); if (m_value != other.m_value) { result |= !!m_value; m_value = JSValue(); @@ -451,8 +464,10 @@ struct AbstractValue { { mergeSpeculation(m_type, type); - if (type & SpecCell) + if (type & SpecCell) { m_structure.makeTop(); + m_unclobberedStructure.makeTop(); + } m_value = JSValue(); checkConsistency(); @@ -462,6 +477,7 @@ struct AbstractValue { { m_type &= other.speculationFromStructures(); m_structure.filter(other); + m_unclobberedStructure.filter(other); // It's possible that prior to the above two statements we had (Foo, TOP), where // Foo is a SpeculatedType that is disjoint with the passed StructureSet. In that @@ -469,6 +485,7 @@ struct AbstractValue { // sure that new information gleaned from the SpeculatedType needs to be fed back // into the information gleaned from the StructureSet. m_structure.filter(m_type); + m_unclobberedStructure.filter(m_type); if (!!m_value && !validateIgnoringValue(m_value)) clear(); @@ -487,6 +504,7 @@ struct AbstractValue { // to ensure that the structure filtering does the right thing is to filter on // the new type (None) rather than the one passed (Array). m_structure.filter(m_type); + m_unclobberedStructure.filter(m_type); if (!!m_value && !validateIgnoringValue(m_value)) clear(); @@ -523,8 +541,8 @@ struct AbstractValue { if (isTop()) return true; - if (!!m_value) - return m_value == value; + if (!!m_value && m_value != value) + return false; if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type) return false; @@ -545,10 +563,39 @@ struct AbstractValue { return true; } + bool validateForEntry(JSValue value) const + { + if (isTop()) + return true; + + if (!!m_value && m_value != value) + return false; + + if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type) + return false; + + if (value.isEmpty()) { + ASSERT(m_type & SpecEmpty); + return true; + } + + if (m_unclobberedStructure.isTop()) + return true; + + if (!!value && value.isCell()) { + ASSERT(m_type & SpecCell); + return m_unclobberedStructure.contains(value.asCell()->structure()); + } + + return true; + } + void checkConsistency() const { - if (!(m_type & SpecCell)) + if (!(m_type & SpecCell)) { ASSERT(m_structure.isClear()); + ASSERT(m_unclobberedStructure.isClear()); + } if (isClear()) ASSERT(!m_value); @@ -566,12 +613,15 @@ struct AbstractValue { { fprintf(out, "(%s, ", speculationToString(m_type)); m_structure.dump(out); + dataLog(", "); + m_unclobberedStructure.dump(out); if (!!m_value) fprintf(out, ", %s", m_value.description()); fprintf(out, ")"); } StructureAbstractValue m_structure; + StructureAbstractValue m_unclobberedStructure; SpeculatedType m_type; JSValue m_value; }; diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index e4e94e90b..b7f48aa4b 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -257,6 +257,8 @@ private: } VariableAccessData* variableAccessData = newVariableAccessData(operand, isCaptured); + variableAccessData->mergeStructureCheckHoistingFailed( + m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache)); NodeIndex nodeIndex = addToGraph(SetLocal, OpInfo(variableAccessData), value); m_currentBlock->variablesAtTail.local(operand) = nodeIndex; } @@ -299,7 +301,8 @@ private: // We're getting an argument in the first basic block; link // the GetLocal to the SetArgument. ASSERT(nodePtr->local() == static_cast<VirtualRegister>(operand)); - nodeIndex = injectLazyOperandSpeculation(addToGraph(GetLocal, OpInfo(nodePtr->variableAccessData()), nodeIndex)); + VariableAccessData* variable = nodePtr->variableAccessData(); + nodeIndex = injectLazyOperandSpeculation(addToGraph(GetLocal, OpInfo(variable), nodeIndex)); m_currentBlock->variablesAtTail.argument(argument) = nodeIndex; return nodeIndex; } @@ -340,6 +343,8 @@ private: flushDirect(operand); VariableAccessData* variableAccessData = newVariableAccessData(operand, isCaptured); + variableAccessData->mergeStructureCheckHoistingFailed( + m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache)); NodeIndex nodeIndex = addToGraph(SetLocal, OpInfo(variableAccessData), value); m_currentBlock->variablesAtTail.argument(argument) = nodeIndex; } @@ -1723,7 +1728,11 @@ bool ByteCodeParser::parseBlock(unsigned limit) if (m_currentBlock == m_graph.m_blocks[0].get() && !m_inlineStackTop->m_inlineCallFrame) { m_graph.m_arguments.resize(m_numArguments); for (unsigned argument = 0; argument < m_numArguments; ++argument) { - NodeIndex setArgument = addToGraph(SetArgument, OpInfo(newVariableAccessData(argumentToOperand(argument), m_codeBlock->argumentIsCaptured(argument)))); + VariableAccessData* variable = newVariableAccessData( + argumentToOperand(argument), m_codeBlock->argumentIsCaptured(argument)); + variable->mergeStructureCheckHoistingFailed( + m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache)); + NodeIndex setArgument = addToGraph(SetArgument, OpInfo(variable)); m_graph.m_arguments[argument] = setArgument; m_currentBlock->variablesAtHead.setArgumentFirstTime(argument, setArgument); m_currentBlock->variablesAtTail.setArgumentFirstTime(argument, setArgument); @@ -2152,10 +2161,14 @@ bool ByteCodeParser::parseBlock(unsigned limit) NodeIndex property = get(currentInstruction[2].u.operand); NodeIndex value = get(currentInstruction[3].u.operand); + bool makeSafe = + m_inlineStackTop->m_profiledBlock->couldTakeSlowCase(m_currentIndex) + || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, OutOfBounds); + addVarArgChild(base); addVarArgChild(property); addVarArgChild(value); - addToGraph(Node::VarArg, PutByVal, OpInfo(0), OpInfo(0)); + addToGraph(Node::VarArg, makeSafe ? PutByValSafe : PutByVal, OpInfo(0), OpInfo(0)); NEXT_OPCODE(op_put_by_val); } @@ -2222,7 +2235,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) } case op_get_by_id: case op_get_by_id_out_of_line: { - SpeculatedType prediction = getPredictionWithoutOSRExit(); + SpeculatedType prediction = getPrediction(); NodeIndex base = get(currentInstruction[2].u.operand); unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[3].u.operand]; @@ -3010,6 +3023,7 @@ void ByteCodeParser::fixVariableAccessSpeculations() VariableAccessData* data = &m_graph.m_variableAccessData[i]; data->find()->predict(data->nonUnifiedPrediction()); data->find()->mergeIsCaptured(data->isCaptured()); + data->find()->mergeStructureCheckHoistingFailed(data->structureCheckHoistingFailed()); } } diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index 4532214ee..e9b1e0d8b 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -284,7 +284,8 @@ private: return index; break; case PutByVal: - case PutByValAlias: { + case PutByValAlias: + case PutByValSafe: { if (!m_graph.byValIsPure(node)) return NoNode; if (m_graph.varArgChild(node, 0) == child1 && canonicalize(m_graph.varArgChild(node, 1)) == canonicalize(child2)) @@ -337,6 +338,7 @@ private: Node& node = m_graph[index]; switch (node.op()) { case CheckStructure: + case ForwardCheckStructure: if (node.child1() == child1 && structureSet.isSupersetOf(node.structureSet())) return true; @@ -362,6 +364,7 @@ private: case PutByVal: case PutByValAlias: + case PutByValSafe: if (m_graph.byValIsPure(node)) { // If PutByVal speculates that it's accessing an array with an // integer index, then it's impossible for it to cause a structure @@ -389,6 +392,7 @@ private: Node& node = m_graph[index]; switch (node.op()) { case CheckStructure: + case ForwardCheckStructure: if (node.child1() == child1 && node.structureSet().containsOnly(structure)) return true; @@ -404,6 +408,7 @@ private: case PutByVal: case PutByValAlias: + case PutByValSafe: if (m_graph.byValIsPure(node)) { // If PutByVal speculates that it's accessing an array with an // integer index, then it's impossible for it to cause a structure @@ -437,6 +442,7 @@ private: break; switch (node.op()) { case CheckStructure: + case ForwardCheckStructure: return NoNode; case PhantomPutStructure: @@ -507,6 +513,7 @@ private: case PutByVal: case PutByValAlias: + case PutByValSafe: if (m_graph.byValIsPure(node)) { // If PutByVal speculates that it's accessing an array with an // integer index, then it's impossible for it to cause a structure @@ -551,6 +558,7 @@ private: case PutByVal: case PutByValAlias: case GetByVal: + case PutByValSafe: if (m_graph.byValIsPure(node)) { // If PutByVal speculates that it's accessing an array with an // integer index, then it's impossible for it to cause a structure @@ -603,6 +611,7 @@ private: case PutByVal: case PutByValAlias: + case PutByValSafe: if (m_graph.byValIsPure(node)) { // If PutByVal speculates that it's accessing an array with an // integer index, then it's impossible for it to cause a structure @@ -643,15 +652,6 @@ private: // change the property storage pointer. break; - case PutByValAlias: - // PutByValAlias can't change the indexed storage pointer - break; - - case PutByVal: - if (isFixedIndexedStorageObjectSpeculation(m_graph[m_graph.varArgChild(node, 0)].prediction()) && m_graph.byValIsPure(node)) - break; - return NoNode; - default: if (m_graph.clobbersWorld(index)) return NoNode; @@ -673,7 +673,7 @@ private: return NoNode; } - NodeIndex getLocalLoadElimination(VirtualRegister local, NodeIndex& relevantLocalOp) + NodeIndex getLocalLoadElimination(VirtualRegister local, NodeIndex& relevantLocalOp, bool careAboutClobbering) { relevantLocalOp = NoNode; @@ -703,7 +703,7 @@ private: break; default: - if (m_graph.clobbersWorld(index)) + if (careAboutClobbering && m_graph.clobbersWorld(index)) return NoNode; break; } @@ -944,13 +944,15 @@ private: case GetLocal: { VariableAccessData* variableAccessData = node.variableAccessData(); - if (!variableAccessData->isCaptured()) + if (m_fixpointState == FixpointNotConverged && !variableAccessData->isCaptured()) break; NodeIndex relevantLocalOp; - NodeIndex possibleReplacement = getLocalLoadElimination(variableAccessData->local(), relevantLocalOp); - ASSERT(relevantLocalOp == NoNode - || m_graph[relevantLocalOp].op() == GetLocalUnlinked - || m_graph[relevantLocalOp].variableAccessData() == variableAccessData); + NodeIndex possibleReplacement = getLocalLoadElimination(variableAccessData->local(), relevantLocalOp, variableAccessData->isCaptured()); + if (relevantLocalOp == NoNode) + break; + if (m_graph[relevantLocalOp].op() != GetLocalUnlinked + && m_graph[relevantLocalOp].variableAccessData() != variableAccessData) + break; NodeIndex phiIndex = node.child1().index(); if (!setReplacement(possibleReplacement)) break; @@ -980,7 +982,7 @@ private: case GetLocalUnlinked: { NodeIndex relevantLocalOpIgnored; - m_changed |= setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored)); + m_changed |= setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored, true)); break; } @@ -1093,7 +1095,8 @@ private: setReplacement(getByValLoadElimination(node.child1().index(), node.child2().index())); break; - case PutByVal: { + case PutByVal: + case PutByValSafe: { Edge child1 = m_graph.varArgChild(node, 0); Edge child2 = m_graph.varArgChild(node, 1); if (isActionableMutableArraySpeculation(m_graph[child1].prediction()) @@ -1108,6 +1111,7 @@ private: } case CheckStructure: + case ForwardCheckStructure: if (checkStructureLoadElimination(node.structureSet(), node.child1().index())) eliminate(); break; diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp index f160b6d35..ddad4f864 100644 --- a/Source/JavaScriptCore/dfg/DFGDriver.cpp +++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp @@ -38,6 +38,7 @@ #include "DFGJITCompiler.h" #include "DFGPredictionPropagationPhase.h" #include "DFGRedundantPhiEliminationPhase.h" +#include "DFGStructureCheckHoistingPhase.h" #include "DFGValidate.h" #include "DFGVirtualRegisterAllocationPhase.h" #include "Options.h" @@ -101,7 +102,10 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo dfg.resetExitStates(); performFixup(dfg); } + bool shouldRedoCFA = performStructureCheckHoisting(dfg); performCSE(dfg, FixpointConverged); + if (shouldRedoCFA) + performCFA(dfg); #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("DFG optimization fixpoint converged in %u iterations.\n", cnt); #endif diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index a1954d7e0..4e3cd5782 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -314,7 +314,8 @@ private: break; } - case PutByVal: { + case PutByVal: + case PutByValSafe: { Edge child1 = m_graph.varArgChild(node, 0); Edge child2 = m_graph.varArgChild(node, 1); Edge child3 = m_graph.varArgChild(node, 2); diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp index c7a4d94d2..9ae0648b8 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.cpp +++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp @@ -207,7 +207,7 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) hasPrinted = !!node.child1(); } - if (node.flags()) { + if (strlen(nodeFlagsAsString(node.flags()))) { dataLog("%s%s", hasPrinted ? ", " : "", nodeFlagsAsString(node.flags())); hasPrinted = true; } @@ -287,13 +287,15 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) dataLog("%sF:#%u", hasPrinted ? ", " : "", node.notTakenBlockIndex()); hasPrinted = true; } + dataLog("%sbc#%u", hasPrinted ? ", " : "", node.codeOrigin.bytecodeIndex); + hasPrinted = true; (void)hasPrinted; dataLog(")"); if (!skipped) { if (node.hasVariableAccessData()) - dataLog(" predicting %s, double ratio %lf%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->doubleVoteRatio(), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : ""); + dataLog(" predicting %s%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : ""); else if (node.hasHeapPrediction()) dataLog(" predicting %s", speculationToString(node.getHeapPrediction())); } diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h index 3e6539353..fdb78cf9b 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.h +++ b/Source/JavaScriptCore/dfg/DFGGraph.h @@ -479,6 +479,15 @@ public: SpeculatedType prediction = at(varArgChild(node, 0)).prediction(); if (!isActionableMutableArraySpeculation(prediction)) return false; + return true; + } + + case PutByValSafe: { + if (!at(varArgChild(node, 1)).shouldSpeculateInteger()) + return false; + SpeculatedType prediction = at(varArgChild(node, 0)).prediction(); + if (!isActionableMutableArraySpeculation(prediction)) + return false; if (isArraySpeculation(prediction)) return false; return true; @@ -524,6 +533,7 @@ public: return !isPredictedNumerical(node); case GetByVal: case PutByVal: + case PutByValSafe: case PutByValAlias: return !byValIsPure(node); default: @@ -568,6 +578,42 @@ public: return node.children.child(index); } + void vote(Edge edge, unsigned ballot) + { + switch (at(edge).op()) { + case ValueToInt32: + case UInt32ToNumber: + edge = at(edge).child1(); + break; + default: + break; + } + + if (at(edge).op() == GetLocal) + at(edge).variableAccessData()->vote(ballot); + } + + void vote(Node& node, unsigned ballot) + { + if (node.flags() & NodeHasVarArgs) { + for (unsigned childIdx = node.firstChild(); + childIdx < node.firstChild() + node.numChildren(); + childIdx++) + vote(m_varArgChildren[childIdx], ballot); + return; + } + + if (!node.child1()) + return; + vote(node.child1(), ballot); + if (!node.child2()) + return; + vote(node.child2(), ballot); + if (!node.child3()) + return; + vote(node.child3(), ballot); + } + JSGlobalData& m_globalData; CodeBlock* m_codeBlock; CodeBlock* m_profiledBlock; diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index e72bd5e36..60cdd4b50 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -245,6 +245,13 @@ struct Node { children.reset(); } + void convertToStructureTransitionWatchpoint() + { + ASSERT(m_op == CheckStructure); + m_opInfo = bitwise_cast<uintptr_t>(structureSet().singletonStructure()); + m_op = StructureTransitionWatchpoint; + } + JSCell* weakConstant() { ASSERT(op() == WeakJSConstant); @@ -651,7 +658,13 @@ struct Node { bool hasStructureSet() { - return op() == CheckStructure; + switch (op()) { + case CheckStructure: + case ForwardCheckStructure: + return true; + default: + return false; + } } StructureSet& structureSet() diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index 0ca0039b9..7657663a9 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -113,12 +113,14 @@ namespace JSC { namespace DFG { /* opcodes use VarArgs beause they may have up to 4 children. */\ macro(GetByVal, NodeResultJS | NodeMustGenerate | NodeMightClobber) \ macro(PutByVal, NodeMustGenerate | NodeHasVarArgs | NodeMightClobber) \ + macro(PutByValSafe, NodeMustGenerate | NodeHasVarArgs | NodeMightClobber) \ macro(PutByValAlias, NodeMustGenerate | NodeHasVarArgs | NodeMightClobber) \ macro(GetById, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \ macro(GetByIdFlush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \ macro(PutById, NodeMustGenerate | NodeClobbersWorld) \ macro(PutByIdDirect, NodeMustGenerate | NodeClobbersWorld) \ macro(CheckStructure, NodeMustGenerate) \ + macro(ForwardCheckStructure, NodeMustGenerate) \ /* Transition watchpoints are a contract between the party setting the watchpoint */\ /* and the runtime system, where the party promises that the child object once had */\ /* the structure being watched, and the runtime system in turn promises that the */\ diff --git a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp index 9a7bc96cc..97061bfb2 100644 --- a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp @@ -99,7 +99,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn else value = exec->argument(argument - 1); - if (!entry->m_expectedValues.argument(argument).validate(value)) { + if (!entry->m_expectedValues.argument(argument).validateForEntry(value)) { #if ENABLE(JIT_VERBOSE_OSR) dataLog(" OSR failed because argument %zu is %s, expected ", argument, value.description()); entry->m_expectedValues.argument(argument).dump(WTF::dataFile()); @@ -119,7 +119,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn } continue; } - if (!entry->m_expectedValues.local(local).validate(exec->registers()[local].jsValue())) { + if (!entry->m_expectedValues.local(local).validateForEntry(exec->registers()[local].jsValue())) { #if ENABLE(JIT_VERBOSE_OSR) dataLog(" OSR failed because variable %zu is %s, expected ", local, exec->registers()[local].jsValue().description()); entry->m_expectedValues.local(local).dump(WTF::dataFile()); diff --git a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp index e9b02b2e3..b3701722e 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp @@ -64,7 +64,7 @@ bool OSRExit::considerAddingAsFrequentExitSiteSlow(CodeBlock* dfgCodeBlock, Code } else exitSite = FrequentExitSite(m_codeOriginForExitProfile.bytecodeIndex, m_kind); - return baselineCodeBlockForOriginAndBaselineCodeBlock(m_codeOrigin, profiledCodeBlock)->addFrequentExitSite(exitSite); + return baselineCodeBlockForOriginAndBaselineCodeBlock(m_codeOriginForExitProfile, profiledCodeBlock)->addFrequentExitSite(exitSite); } } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 94479d6d0..882e1cd02 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -423,12 +423,15 @@ EncodedJSValue DFG_OPERATION operationGetByIdBuildListWithReturnAddress(ExecStat JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue); JSValue result = baseValue.get(exec, *propertyName, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); - dfgBuildGetByIDList(exec, baseValue, *propertyName, slot, stubInfo); + if (accessType == static_cast<AccessType>(stubInfo.accessType)) + dfgBuildGetByIDList(exec, baseValue, *propertyName, slot, stubInfo); return JSValue::encode(result); } @@ -439,12 +442,15 @@ EncodedJSValue DFG_OPERATION operationGetByIdProtoBuildListWithReturnAddress(Exe JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue); JSValue result = baseValue.get(exec, *propertyName, slot); - - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); - dfgBuildGetByIDProtoList(exec, baseValue, *propertyName, slot, stubInfo); + + if (accessType == static_cast<AccessType>(stubInfo.accessType)) + dfgBuildGetByIDProtoList(exec, baseValue, *propertyName, slot, stubInfo); return JSValue::encode(result); } @@ -455,15 +461,19 @@ EncodedJSValue DFG_OPERATION operationGetByIdOptimizeWithReturnAddress(ExecState JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue); JSValue result = baseValue.get(exec, *propertyName, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); - if (stubInfo.seen) - dfgRepatchGetByID(exec, baseValue, *propertyName, slot, stubInfo); - else - stubInfo.seen = true; + if (accessType == static_cast<AccessType>(stubInfo.accessType)) { + if (stubInfo.seen) + dfgRepatchGetByID(exec, baseValue, *propertyName, slot, stubInfo); + else + stubInfo.seen = true; + } return JSValue::encode(result); } @@ -645,13 +655,18 @@ void DFG_OPERATION operationPutByIdStrictOptimizeWithReturnAddress(ExecState* ex JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue value = JSValue::decode(encodedValue); JSValue baseValue(base); PutPropertySlot slot(true); baseValue.put(exec, *propertyName, value, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + if (accessType != static_cast<AccessType>(stubInfo.accessType)) + return; + if (stubInfo.seen) dfgRepatchPutByID(exec, baseValue, *propertyName, slot, stubInfo, NotDirect); else @@ -664,13 +679,18 @@ void DFG_OPERATION operationPutByIdNonStrictOptimizeWithReturnAddress(ExecState* JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue value = JSValue::decode(encodedValue); JSValue baseValue(base); PutPropertySlot slot(false); baseValue.put(exec, *propertyName, value, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + if (accessType != static_cast<AccessType>(stubInfo.accessType)) + return; + if (stubInfo.seen) dfgRepatchPutByID(exec, baseValue, *propertyName, slot, stubInfo, NotDirect); else @@ -683,13 +703,18 @@ void DFG_OPERATION operationPutByIdDirectStrictOptimizeWithReturnAddress(ExecSta JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue value = JSValue::decode(encodedValue); PutPropertySlot slot(true); ASSERT(base->isObject()); asObject(base)->putDirect(exec->globalData(), *propertyName, value, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + if (accessType != static_cast<AccessType>(stubInfo.accessType)) + return; + if (stubInfo.seen) dfgRepatchPutByID(exec, base, *propertyName, slot, stubInfo, Direct); else @@ -702,13 +727,18 @@ void DFG_OPERATION operationPutByIdDirectNonStrictOptimizeWithReturnAddress(Exec JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue value = JSValue::decode(encodedValue); PutPropertySlot slot(false); ASSERT(base->isObject()); asObject(base)->putDirect(exec->globalData(), *propertyName, value, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + if (accessType != static_cast<AccessType>(stubInfo.accessType)) + return; + if (stubInfo.seen) dfgRepatchPutByID(exec, base, *propertyName, slot, stubInfo, Direct); else @@ -721,13 +751,18 @@ void DFG_OPERATION operationPutByIdStrictBuildListWithReturnAddress(ExecState* e JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue value = JSValue::decode(encodedValue); JSValue baseValue(base); PutPropertySlot slot(true); baseValue.put(exec, *propertyName, value, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + if (accessType != static_cast<AccessType>(stubInfo.accessType)) + return; + dfgBuildPutByIdList(exec, baseValue, *propertyName, slot, stubInfo, NotDirect); } @@ -737,13 +772,18 @@ void DFG_OPERATION operationPutByIdNonStrictBuildListWithReturnAddress(ExecState JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue value = JSValue::decode(encodedValue); JSValue baseValue(base); PutPropertySlot slot(false); baseValue.put(exec, *propertyName, value, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + if (accessType != static_cast<AccessType>(stubInfo.accessType)) + return; + dfgBuildPutByIdList(exec, baseValue, *propertyName, slot, stubInfo, NotDirect); } @@ -753,13 +793,18 @@ void DFG_OPERATION operationPutByIdDirectStrictBuildListWithReturnAddress(ExecSt JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue value = JSValue::decode(encodedValue); PutPropertySlot slot(true); ASSERT(base->isObject()); asObject(base)->putDirect(exec->globalData(), *propertyName, value, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + if (accessType != static_cast<AccessType>(stubInfo.accessType)) + return; + dfgBuildPutByIdList(exec, base, *propertyName, slot, stubInfo, Direct); } @@ -769,13 +814,18 @@ void DFG_OPERATION operationPutByIdDirectNonStrictBuildListWithReturnAddress(Exe JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + AccessType accessType = static_cast<AccessType>(stubInfo.accessType); + JSValue value = JSValue::decode(encodedValue); PutPropertySlot slot(false); ASSERT(base->isObject()); asObject(base)->putDirect(exec->globalData(), *propertyName, value, slot); - StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress); + if (accessType != static_cast<AccessType>(stubInfo.accessType)) + return; + dfgBuildPutByIdList(exec, base, *propertyName, slot, stubInfo, Direct); } diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index 2e4f8094c..94f69abc2 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -637,6 +637,7 @@ private: } case PutByVal: + case PutByValSafe: changed |= m_graph[m_graph.varArgChild(node, 0)].mergeFlags(NodeUsedAsValue); changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); changed |= m_graph[m_graph.varArgChild(node, 2)].mergeFlags(NodeUsedAsValue); @@ -672,6 +673,7 @@ private: case ForceOSRExit: case SetArgument: case CheckStructure: + case ForwardCheckStructure: case StructureTransitionWatchpoint: case CheckFunction: case PutStructure: @@ -746,42 +748,6 @@ private: propagate(m_graph[m_compileIndex]); } - void vote(Edge nodeUse, VariableAccessData::Ballot ballot) - { - switch (m_graph[nodeUse].op()) { - case ValueToInt32: - case UInt32ToNumber: - nodeUse = m_graph[nodeUse].child1(); - break; - default: - break; - } - - if (m_graph[nodeUse].op() == GetLocal) - m_graph[nodeUse].variableAccessData()->vote(ballot); - } - - void vote(Node& node, VariableAccessData::Ballot ballot) - { - if (node.flags() & NodeHasVarArgs) { - for (unsigned childIdx = node.firstChild(); - childIdx < node.firstChild() + node.numChildren(); - childIdx++) - vote(m_graph.m_varArgChildren[childIdx], ballot); - return; - } - - if (!node.child1()) - return; - vote(node.child1(), ballot); - if (!node.child2()) - return; - vote(node.child2(), ballot); - if (!node.child3()) - return; - vote(node.child3(), ballot); - } - void doRoundOfDoubleVoting() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) @@ -798,16 +764,16 @@ private: SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); - VariableAccessData::Ballot ballot; + DoubleBallot ballot; if (isNumberSpeculation(left) && isNumberSpeculation(right) && !m_graph.addShouldSpeculateInteger(node)) - ballot = VariableAccessData::VoteDouble; + ballot = VoteDouble; else - ballot = VariableAccessData::VoteValue; + ballot = VoteValue; - vote(node.child1(), ballot); - vote(node.child2(), ballot); + m_graph.vote(node.child1(), ballot); + m_graph.vote(node.child2(), ballot); break; } @@ -815,16 +781,16 @@ private: SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); - VariableAccessData::Ballot ballot; + DoubleBallot ballot; if (isNumberSpeculation(left) && isNumberSpeculation(right) && !m_graph.mulShouldSpeculateInteger(node)) - ballot = VariableAccessData::VoteDouble; + ballot = VoteDouble; else - ballot = VariableAccessData::VoteValue; + ballot = VoteValue; - vote(node.child1(), ballot); - vote(node.child2(), ballot); + m_graph.vote(node.child1(), ballot); + m_graph.vote(node.child2(), ballot); break; } @@ -835,46 +801,46 @@ private: SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); - VariableAccessData::Ballot ballot; + DoubleBallot ballot; if (isNumberSpeculation(left) && isNumberSpeculation(right) && !(Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child1()]) && node.canSpeculateInteger())) - ballot = VariableAccessData::VoteDouble; + ballot = VoteDouble; else - ballot = VariableAccessData::VoteValue; + ballot = VoteValue; - vote(node.child1(), ballot); - vote(node.child2(), ballot); + m_graph.vote(node.child1(), ballot); + m_graph.vote(node.child2(), ballot); break; } case ArithAbs: - VariableAccessData::Ballot ballot; + DoubleBallot ballot; if (!(m_graph[node.child1()].shouldSpeculateInteger() && node.canSpeculateInteger())) - ballot = VariableAccessData::VoteDouble; + ballot = VoteDouble; else - ballot = VariableAccessData::VoteValue; + ballot = VoteValue; - vote(node.child1(), ballot); + m_graph.vote(node.child1(), ballot); break; case ArithSqrt: - vote(node.child1(), VariableAccessData::VoteDouble); + m_graph.vote(node.child1(), VoteDouble); break; case SetLocal: { SpeculatedType prediction = m_graph[node.child1()].prediction(); if (isDoubleSpeculation(prediction)) - node.variableAccessData()->vote(VariableAccessData::VoteDouble); + node.variableAccessData()->vote(VoteDouble); else if (!isNumberSpeculation(prediction) || isInt32Speculation(prediction)) - node.variableAccessData()->vote(VariableAccessData::VoteValue); + node.variableAccessData()->vote(VoteValue); break; } default: - vote(node, VariableAccessData::VoteValue); + m_graph.vote(node, VoteValue); break; } } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 179ad45fa..f17e2d7e4 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -56,6 +56,163 @@ SpeculativeJIT::~SpeculativeJIT() WTF::deleteAllValues(m_slowPathGenerators); } +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail) +{ + if (!m_compileOkay) + return; + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size())); +} + +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + speculationCheck(kind, jsValueSource, nodeUse.index(), jumpToFail); +} + +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + Vector<MacroAssembler::Jump, 16> jumpVector = jumpsToFail.jumps(); + for (unsigned i = 0; i < jumpVector.size(); ++i) + speculationCheck(kind, jsValueSource, nodeIndex, jumpVector[i]); +} + +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::JumpList& jumpsToFail) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + speculationCheck(kind, jsValueSource, nodeUse.index(), jumpsToFail); +} + +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) +{ + if (!m_compileOkay) + return; + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + m_jit.codeBlock()->appendSpeculationRecovery(recovery); + m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size(), m_jit.codeBlock()->numberOfSpeculationRecoveries())); +} + +void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + speculationCheck(kind, jsValueSource, nodeUse.index(), jumpToFail, recovery); +} + +JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpoint(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex) +{ + if (!m_compileOkay) + return 0; + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + OSRExit& exit = m_jit.codeBlock()->osrExit( + m_jit.codeBlock()->appendOSRExit( + OSRExit(kind, jsValueSource, + m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), + JITCompiler::Jump(), this, m_stream->size()))); + exit.m_watchpointIndex = m_jit.codeBlock()->appendWatchpoint( + JumpReplacementWatchpoint(m_jit.watchpointLabel())); + return &m_jit.codeBlock()->watchpoint(exit.m_watchpointIndex); +} + +JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpoint(ExitKind kind) +{ + return speculationWatchpoint(kind, JSValueSource(), NoNode); +} + +void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + speculationCheck(kind, jsValueSource, nodeIndex, jumpToFail); + +#if !ASSERT_DISABLED + if (!valueRecovery) { + // Check that the preceding node was a SetLocal with the same code origin. + Node* setLocal = &at(m_jit.graph().m_blocks[m_block]->at(m_indexInBlock - 1)); + ASSERT(setLocal->op() == SetLocal); + ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin); + } +#endif + + unsigned setLocalIndexInBlock = m_indexInBlock + 1; + + Node* setLocal = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock)); + bool hadInt32ToDouble = false; + + if (setLocal->op() == Int32ToDouble) { + setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock)); + hadInt32ToDouble = true; + } + if (setLocal->op() == Flush || setLocal->op() == Phantom) + setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock)); + + if (!!valueRecovery) { + if (hadInt32ToDouble) + ASSERT(at(setLocal->child1()).child1() == m_compileIndex); + else + ASSERT(setLocal->child1() == m_compileIndex); + } + ASSERT(setLocal->op() == SetLocal); + ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin); + + Node* nextNode = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 1)); + ASSERT(nextNode->codeOrigin != at(m_compileIndex).codeOrigin); + + OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); + exit.m_codeOrigin = nextNode->codeOrigin; + + if (!valueRecovery) + return; + exit.m_lastSetOperand = setLocal->local(); + exit.m_valueRecoveryOverride = adoptRef( + new ValueRecoveryOverride(setLocal->local(), valueRecovery)); +} + +void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& valueRecovery) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + Vector<MacroAssembler::Jump, 16> jumpVector = jumpsToFail.jumps(); + for (unsigned i = 0; i < jumpVector.size(); ++i) + forwardSpeculationCheck(kind, jsValueSource, nodeIndex, jumpVector[i], valueRecovery); +} + +void SpeculativeJIT::speculationCheckWithConditionalDirection(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, bool isForward) +{ + if (isForward) + forwardSpeculationCheck(kind, jsValueSource, nodeIndex, jumpToFail); + else + speculationCheck(kind, jsValueSource, nodeIndex, jumpToFail); +} + +void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, NodeIndex nodeIndex) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); +#if DFG_ENABLE(DEBUG_VERBOSE) + dataLog("SpeculativeJIT was terminated.\n"); +#endif + if (!m_compileOkay) + return; + speculationCheck(kind, jsValueRegs, nodeIndex, m_jit.jump()); + m_compileOkay = false; +} + +void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Edge nodeUse) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); + terminateSpeculativeExecution(kind, jsValueRegs, nodeUse.index()); +} + +void SpeculativeJIT::terminateSpeculativeExecutionWithConditionalDirection(ExitKind kind, JSValueRegs jsValueRegs, NodeIndex nodeIndex, bool isForward) +{ + ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); +#if DFG_ENABLE(DEBUG_VERBOSE) + dataLog("SpeculativeJIT was terminated.\n"); +#endif + if (!m_compileOkay) + return; + speculationCheckWithConditionalDirection(kind, jsValueRegs, nodeIndex, m_jit.jump(), isForward); + m_compileOkay = false; +} + void SpeculativeJIT::addSlowPathGenerator(PassOwnPtr<SlowPathGenerator> slowPathGenerator) { m_slowPathGenerators.append(slowPathGenerator.leakPtr()); @@ -2902,7 +3059,7 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) ASSERT_NOT_REACHED(); } else if (at(node.child1()).prediction() == SpecString) { if (!isStringSpeculation(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(&JSString::s_info))); + speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), storageReg); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index f43323afd..96f2fec0a 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -306,7 +306,7 @@ public: GPRReg fillSpeculateInt(NodeIndex, DataFormat& returnFormat); GPRReg fillSpeculateIntStrict(NodeIndex); FPRReg fillSpeculateDouble(NodeIndex); - GPRReg fillSpeculateCell(NodeIndex); + GPRReg fillSpeculateCell(NodeIndex, bool isForwardSpeculation = false); GPRReg fillSpeculateBoolean(NodeIndex); GeneratedOperandType checkGeneratedTypeForToInt32(NodeIndex); @@ -2163,130 +2163,33 @@ public: #endif // Add a speculation check without additional recovery. - void speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail) - { - if (!m_compileOkay) - return; - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size())); - } - void speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail) - { - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - speculationCheck(kind, jsValueSource, nodeUse.index(), jumpToFail); - } + void speculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::Jump jumpToFail); + void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::Jump jumpToFail); // Add a set of speculation checks without additional recovery. - void speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail) - { - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - Vector<MacroAssembler::Jump, 16> jumpVector = jumpsToFail.jumps(); - for (unsigned i = 0; i < jumpVector.size(); ++i) - speculationCheck(kind, jsValueSource, nodeIndex, jumpVector[i]); - } - void speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::JumpList& jumpsToFail) - { - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - speculationCheck(kind, jsValueSource, nodeUse.index(), jumpsToFail); - } + void speculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::JumpList& jumpsToFail); + void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::JumpList& jumpsToFail); // Add a speculation check with additional recovery. - void speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) - { - if (!m_compileOkay) - return; - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - m_jit.codeBlock()->appendSpeculationRecovery(recovery); - m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size(), m_jit.codeBlock()->numberOfSpeculationRecoveries())); - } - void speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) - { - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - speculationCheck(kind, jsValueSource, nodeUse.index(), jumpToFail, recovery); - } + void speculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&); + void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&); // Use this like you would use speculationCheck(), except that you don't pass it a jump // (because you don't have to execute a branch; that's kind of the whole point), and you // must register the returned Watchpoint with something relevant. In general, this should // be used with extreme care. Use speculationCheck() unless you've got an amazing reason // not to. - JumpReplacementWatchpoint* speculationWatchpoint(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex) - { - if (!m_compileOkay) - return 0; - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - OSRExit& exit = m_jit.codeBlock()->osrExit( - m_jit.codeBlock()->appendOSRExit( - OSRExit(kind, jsValueSource, - m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), - JITCompiler::Jump(), this, m_stream->size()))); - exit.m_watchpointIndex = m_jit.codeBlock()->appendWatchpoint( - JumpReplacementWatchpoint(m_jit.watchpointLabel())); - return &m_jit.codeBlock()->watchpoint(exit.m_watchpointIndex); - } + JumpReplacementWatchpoint* speculationWatchpoint(ExitKind, JSValueSource, NodeIndex); // The default for speculation watchpoints is that they're uncounted, because the // act of firing a watchpoint invalidates it. So, future recompilations will not // attempt to set this watchpoint again. - JumpReplacementWatchpoint* speculationWatchpoint() - { - return speculationWatchpoint(UncountableWatchpoint, JSValueSource(), NoNode); - } - void forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery) - { - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - speculationCheck(kind, jsValueSource, nodeIndex, jumpToFail); - - unsigned setLocalIndexInBlock = m_indexInBlock + 1; - - Node* setLocal = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock)); - bool hadInt32ToDouble = false; - - if (setLocal->op() == Int32ToDouble) { - setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock)); - hadInt32ToDouble = true; - } - if (setLocal->op() == Flush || setLocal->op() == Phantom) - setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock)); - - if (hadInt32ToDouble) - ASSERT(at(setLocal->child1()).child1() == m_compileIndex); - else - ASSERT(setLocal->child1() == m_compileIndex); - ASSERT(setLocal->op() == SetLocal); - ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin); - - Node* nextNode = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 1)); - ASSERT(nextNode->codeOrigin != at(m_compileIndex).codeOrigin); - - OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); - exit.m_codeOrigin = nextNode->codeOrigin; - exit.m_lastSetOperand = setLocal->local(); - - exit.m_valueRecoveryOverride = adoptRef( - new ValueRecoveryOverride(setLocal->local(), valueRecovery)); - } - void forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& valueRecovery) - { - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - Vector<MacroAssembler::Jump, 16> jumpVector = jumpsToFail.jumps(); - for (unsigned i = 0; i < jumpVector.size(); ++i) - forwardSpeculationCheck(kind, jsValueSource, nodeIndex, jumpVector[i], valueRecovery); - } - + JumpReplacementWatchpoint* speculationWatchpoint(ExitKind = UncountableWatchpoint); + // Note: not specifying the valueRecovery argument (leaving it as ValueRecovery()) implies + // that you've ensured that there exists a MovHint prior to your use of forwardSpeculationCheck(). + void forwardSpeculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& = ValueRecovery()); + void forwardSpeculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& = ValueRecovery()); + void speculationCheckWithConditionalDirection(ExitKind, JSValueSource, NodeIndex, MacroAssembler::Jump jumpToFail, bool isForward); // Called when we statically determine that a speculation will fail. - void terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, NodeIndex nodeIndex) - { - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); -#if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpeculativeJIT was terminated.\n"); -#endif - if (!m_compileOkay) - return; - speculationCheck(kind, jsValueRegs, nodeIndex, m_jit.jump()); - m_compileOkay = false; - } - void terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Edge nodeUse) - { - ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - terminateSpeculativeExecution(kind, jsValueRegs, nodeUse.index()); - } + void terminateSpeculativeExecution(ExitKind, JSValueRegs, NodeIndex); + void terminateSpeculativeExecution(ExitKind, JSValueRegs, Edge); + void terminateSpeculativeExecutionWithConditionalDirection(ExitKind, JSValueRegs, NodeIndex, bool isForward); template<bool strict> GPRReg fillSpeculateIntInternal(NodeIndex, DataFormat& returnFormat); @@ -2912,10 +2815,11 @@ private: class SpeculateCellOperand { public: - explicit SpeculateCellOperand(SpeculativeJIT* jit, Edge use) + explicit SpeculateCellOperand(SpeculativeJIT* jit, Edge use, bool isForwardSpeculation = false) : m_jit(jit) , m_index(use.index()) , m_gprOrInvalid(InvalidGPRReg) + , m_isForwardSpeculation(isForwardSpeculation) { ASSERT(m_jit); ASSERT(use.useKind() != DoubleUse); @@ -2937,7 +2841,7 @@ public: GPRReg gpr() { if (m_gprOrInvalid == InvalidGPRReg) - m_gprOrInvalid = m_jit->fillSpeculateCell(index()); + m_gprOrInvalid = m_jit->fillSpeculateCell(index(), m_isForwardSpeculation); return m_gprOrInvalid; } @@ -2950,6 +2854,7 @@ private: SpeculativeJIT* m_jit; NodeIndex m_index; GPRReg m_gprOrInvalid; + bool m_isForwardSpeculation; }; class SpeculateBooleanOperand { diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 6209da485..bf3503d6d 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -1284,13 +1284,13 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) } } -GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) +GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation) { #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("SpecCell@%d ", nodeIndex); #endif if (isKnownNotCell(nodeIndex)) { - terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation); return allocate(); } @@ -1314,7 +1314,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) ASSERT((info.spillFormat() & DataFormatJS) || info.spillFormat() == DataFormatCell); if (!isCellSpeculation(type)) - speculationCheck(BadType, JSValueSource(JITCompiler::addressFor(virtualRegister)), nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::CellTag))); + speculationCheckWithConditionalDirection(BadType, JSValueSource(JITCompiler::addressFor(virtualRegister)), nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::CellTag)), isForwardSpeculation); GPRReg gpr = allocate(); m_jit.load32(JITCompiler::payloadFor(virtualRegister), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); @@ -1335,7 +1335,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) m_gprs.lock(tagGPR); m_gprs.lock(payloadGPR); if (!isCellSpeculation(type)) - speculationCheck(BadType, JSValueRegs(tagGPR, payloadGPR), nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, tagGPR, TrustedImm32(JSValue::CellTag))); + speculationCheckWithConditionalDirection(BadType, JSValueRegs(tagGPR, payloadGPR), nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, tagGPR, TrustedImm32(JSValue::CellTag)), isForwardSpeculation); m_gprs.unlock(tagGPR); m_gprs.release(tagGPR); m_gprs.release(payloadGPR); @@ -2483,7 +2483,8 @@ void SpeculativeJIT::compile(Node& node) break; } - case PutByVal: { + case PutByVal: + case PutByValSafe: { Edge child1 = m_jit.graph().varArgChild(node, 0); Edge child2 = m_jit.graph().varArgChild(node, 1); Edge child3 = m_jit.graph().varArgChild(node, 2); @@ -2599,12 +2600,14 @@ void SpeculativeJIT::compile(Node& node) if (!isArraySpeculation(m_state.forNode(child1).m_type)) speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), child1, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())); + if (node.op() == PutByVal) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds); + base.use(); property.use(); value.use(); - MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())); - // Get the array storage. GPRReg storageReg = scratchReg; m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); @@ -2626,12 +2629,14 @@ void SpeculativeJIT::compile(Node& node) m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); - addSlowPathGenerator( - slowPathCall( - beyondArrayBounds, this, - m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, - NoResult, baseReg, propertyReg, valueTagReg, valuePayloadReg)); - + if (node.op() == PutByValSafe) { + addSlowPathGenerator( + slowPathCall( + beyondArrayBounds, this, + m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, + NoResult, baseReg, propertyReg, valueTagReg, valuePayloadReg)); + } + noResult(m_compileIndex, UseChildrenCalledExplicitly); break; } @@ -2990,7 +2995,7 @@ void SpeculativeJIT::compile(Node& node) m_jit.move(op1PayloadGPR, resultPayloadGPR); } else { MacroAssembler::Jump alreadyPrimitive = m_jit.branch32(MacroAssembler::NotEqual, op1TagGPR, TrustedImm32(JSValue::CellTag)); - MacroAssembler::Jump notPrimitive = m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1PayloadGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSString::s_info)); + MacroAssembler::Jump notPrimitive = m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1PayloadGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get())); alreadyPrimitive.link(&m_jit); m_jit.move(op1TagGPR, resultTagGPR); @@ -3174,7 +3179,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg thisValueGPR = thisValue.gpr(); if (!isObjectSpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(thisValueGPR), node.child1(), m_jit.branchPtr(JITCompiler::Equal, JITCompiler::Address(thisValueGPR, JSCell::classInfoOffset()), JITCompiler::TrustedImmPtr(&JSString::s_info))); + speculationCheck(BadType, JSValueSource::unboxedCell(thisValueGPR), node.child1(), m_jit.branchPtr(JITCompiler::Equal, JITCompiler::Address(thisValueGPR, JSCell::structureOffset()), JITCompiler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); GPRTemporary result(this, thisValue); GPRReg resultGPR = result.gpr(); @@ -3418,7 +3423,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); if (!isStringSpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSString::s_info))); + speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); m_jit.load32(MacroAssembler::Address(baseGPR, JSString::offsetOfLength()), resultGPR); @@ -3470,7 +3475,8 @@ void SpeculativeJIT::compile(Node& node) break; } - case CheckStructure: { + case CheckStructure: + case ForwardCheckStructure: { AbstractValue& value = m_state.forNode(node.child1()); if (value.m_structure.isSubsetOf(node.structureSet()) && isCellSpeculation(value.m_type)) { @@ -3478,13 +3484,19 @@ void SpeculativeJIT::compile(Node& node) break; } - SpeculateCellOperand base(this, node.child1()); + SpeculateCellOperand base(this, node.child1(), node.op() == ForwardCheckStructure); ASSERT(node.structureSet().size()); - if (node.structureSet().size() == 1) - speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, JITCompiler::Address(base.gpr(), JSCell::structureOffset()), node.structureSet()[0])); - else { + if (node.structureSet().size() == 1) { + speculationCheckWithConditionalDirection( + BadCache, JSValueRegs(), NoNode, + m_jit.branchWeakPtr( + JITCompiler::NotEqual, + JITCompiler::Address(base.gpr(), JSCell::structureOffset()), + node.structureSet()[0]), + node.op() == ForwardCheckStructure); + } else { GPRTemporary structure(this); m_jit.loadPtr(JITCompiler::Address(base.gpr(), JSCell::structureOffset()), structure.gpr()); @@ -3494,7 +3506,11 @@ void SpeculativeJIT::compile(Node& node) for (size_t i = 0; i < node.structureSet().size() - 1; ++i) done.append(m_jit.branchWeakPtr(JITCompiler::Equal, structure.gpr(), node.structureSet()[i])); - speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, structure.gpr(), node.structureSet().last())); + speculationCheckWithConditionalDirection( + BadCache, JSValueRegs(), NoNode, + m_jit.branchWeakPtr( + JITCompiler::NotEqual, structure.gpr(), node.structureSet().last()), + node.op() == ForwardCheckStructure); done.link(&m_jit); } @@ -3505,7 +3521,7 @@ void SpeculativeJIT::compile(Node& node) case StructureTransitionWatchpoint: { m_jit.addWeakReference(node.structure()); - node.structure()->addTransitionWatchpoint(speculationWatchpoint()); + node.structure()->addTransitionWatchpoint(speculationWatchpoint(BadCache)); #if !ASSERT_DISABLED SpeculateCellOperand op1(this, node.child1()); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index 39f4a0740..5541113f2 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -1326,7 +1326,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) } } -GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) +GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation) { #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("SpecCell@%d ", nodeIndex); @@ -1339,7 +1339,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) switch (info.registerFormat()) { case DataFormatNone: { if (info.spillFormat() == DataFormatInteger || info.spillFormat() == DataFormatDouble) { - terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation); return allocate(); } @@ -1353,7 +1353,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) info.fillJSValue(*m_stream, gpr, DataFormatJSCell); return gpr; } - terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation); return gpr; } ASSERT(info.spillFormat() & DataFormatJS); @@ -1362,7 +1362,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) info.fillJSValue(*m_stream, gpr, DataFormatJS); if (!isCellSpeculation(type)) - speculationCheck(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister)); + speculationCheckWithConditionalDirection(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister), isForwardSpeculation); info.fillJSValue(*m_stream, gpr, DataFormatJSCell); return gpr; } @@ -1378,7 +1378,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) GPRReg gpr = info.gpr(); m_gprs.lock(gpr); if (!isCellSpeculation(type)) - speculationCheck(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister)); + speculationCheckWithConditionalDirection(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister), isForwardSpeculation); info.fillJSValue(*m_stream, gpr, DataFormatJSCell); return gpr; } @@ -1389,7 +1389,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) case DataFormatDouble: case DataFormatJSBoolean: case DataFormatBoolean: { - terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); + terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation); return allocate(); } @@ -1578,7 +1578,7 @@ void SpeculativeJIT::compileObjectToObjectOrOtherEquality( // We know that within this branch, rightChild must not be a cell. Check if that is enough to // prove that it is either null or undefined. - if (!isOtherSpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) { + if (!isOtherOrEmptySpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) { m_jit.move(op2GPR, resultGPR); m_jit.andPtr(MacroAssembler::TrustedImm32(~TagBitUndefined), resultGPR); @@ -1649,7 +1649,7 @@ void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality( // We know that within this branch, rightChild must not be a cell. Check if that is enough to // prove that it is either null or undefined. - if (isOtherSpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) + if (isOtherOrEmptySpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) rightNotCell.link(&m_jit); else { jump(notTaken, ForceJump); @@ -2508,7 +2508,8 @@ void SpeculativeJIT::compile(Node& node) break; } - case PutByVal: { + case PutByVal: + case PutByValSafe: { Edge child1 = m_jit.graph().varArgChild(node, 0); Edge child2 = m_jit.graph().varArgChild(node, 1); Edge child3 = m_jit.graph().varArgChild(node, 2); @@ -2675,13 +2676,15 @@ void SpeculativeJIT::compile(Node& node) // If we have predicted the base to be type array, we can skip the check. if (!isArraySpeculation(m_state.forNode(child1).m_type)) speculationCheck(BadType, JSValueRegs(baseReg), child1, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); + + MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())); + if (node.op() == PutByVal) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds); base.use(); property.use(); value.use(); - MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())); - // Get the array storage. GPRReg storageReg = scratchReg; m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg); @@ -2702,11 +2705,13 @@ void SpeculativeJIT::compile(Node& node) // Store the value to the array. m_jit.storePtr(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); - addSlowPathGenerator( - slowPathCall( - beyondArrayBounds, this, - m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, - NoResult, baseReg, propertyReg, valueReg)); + if (node.op() == PutByValSafe) { + addSlowPathGenerator( + slowPathCall( + beyondArrayBounds, this, + m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, + NoResult, baseReg, propertyReg, valueReg)); + } noResult(m_compileIndex, UseChildrenCalledExplicitly); break; @@ -3043,7 +3048,7 @@ void SpeculativeJIT::compile(Node& node) m_jit.move(op1GPR, resultGPR); else { MacroAssembler::Jump alreadyPrimitive = m_jit.branchTestPtr(MacroAssembler::NonZero, op1GPR, GPRInfo::tagMaskRegister); - MacroAssembler::Jump notPrimitive = m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSString::s_info)); + MacroAssembler::Jump notPrimitive = m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1GPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get())); alreadyPrimitive.link(&m_jit); m_jit.move(op1GPR, resultGPR); @@ -3208,7 +3213,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); if (!isObjectSpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueRegs(thisValueGPR), node.child1(), m_jit.branchPtr(JITCompiler::Equal, JITCompiler::Address(thisValueGPR, JSCell::classInfoOffset()), JITCompiler::TrustedImmPtr(&JSString::s_info))); + speculationCheck(BadType, JSValueRegs(thisValueGPR), node.child1(), m_jit.branchPtr(JITCompiler::Equal, JITCompiler::Address(thisValueGPR, JSCell::structureOffset()), JITCompiler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); m_jit.move(thisValueGPR, resultGPR); @@ -3435,7 +3440,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); if (!isStringSpeculation(m_state.forNode(node.child1()).m_type)) - speculationCheck(BadType, JSValueRegs(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSString::s_info))); + speculationCheck(BadType, JSValueRegs(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()))); m_jit.load32(MacroAssembler::Address(baseGPR, JSString::offsetOfLength()), resultGPR); @@ -3485,7 +3490,8 @@ void SpeculativeJIT::compile(Node& node) noResult(m_compileIndex); break; } - case CheckStructure: { + case CheckStructure: + case ForwardCheckStructure: { AbstractValue& value = m_state.forNode(node.child1()); if (value.m_structure.isSubsetOf(node.structureSet()) && isCellSpeculation(value.m_type)) { @@ -3493,13 +3499,19 @@ void SpeculativeJIT::compile(Node& node) break; } - SpeculateCellOperand base(this, node.child1()); + SpeculateCellOperand base(this, node.child1(), node.op() == ForwardCheckStructure); ASSERT(node.structureSet().size()); - if (node.structureSet().size() == 1) - speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, JITCompiler::Address(base.gpr(), JSCell::structureOffset()), node.structureSet()[0])); - else { + if (node.structureSet().size() == 1) { + speculationCheckWithConditionalDirection( + BadCache, JSValueRegs(), NoNode, + m_jit.branchWeakPtr( + JITCompiler::NotEqual, + JITCompiler::Address(base.gpr(), JSCell::structureOffset()), + node.structureSet()[0]), + node.op() == ForwardCheckStructure); + } else { GPRTemporary structure(this); m_jit.loadPtr(JITCompiler::Address(base.gpr(), JSCell::structureOffset()), structure.gpr()); @@ -3509,7 +3521,11 @@ void SpeculativeJIT::compile(Node& node) for (size_t i = 0; i < node.structureSet().size() - 1; ++i) done.append(m_jit.branchWeakPtr(JITCompiler::Equal, structure.gpr(), node.structureSet()[i])); - speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, structure.gpr(), node.structureSet().last())); + speculationCheckWithConditionalDirection( + BadCache, JSValueRegs(), NoNode, + m_jit.branchWeakPtr( + JITCompiler::NotEqual, structure.gpr(), node.structureSet().last()), + node.op() == ForwardCheckStructure); done.link(&m_jit); } @@ -3520,7 +3536,7 @@ void SpeculativeJIT::compile(Node& node) case StructureTransitionWatchpoint: { m_jit.addWeakReference(node.structure()); - node.structure()->addTransitionWatchpoint(speculationWatchpoint()); + node.structure()->addTransitionWatchpoint(speculationWatchpoint(BadCache)); #if !ASSERT_DISABLED SpeculateCellOperand op1(this, node.child1()); diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp new file mode 100644 index 000000000..e86c57dff --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp @@ -0,0 +1,490 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 "DFGStructureCheckHoistingPhase.h" + +#if ENABLE(DFG_JIT) + +#include "DFGBasicBlock.h" +#include "DFGGraph.h" +#include "DFGInsertionSet.h" +#include "DFGPhase.h" +#include <wtf/HashMap.h> + +namespace JSC { namespace DFG { + +enum CheckBallot { VoteOther, VoteStructureCheck }; + +class StructureCheckHoistingPhase : public Phase { +public: + StructureCheckHoistingPhase(Graph& graph) + : Phase(graph, "structure check hoisting") + { + } + + bool run() + { + for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { + VariableAccessData* variable = &m_graph.m_variableAccessData[i]; + if (!variable->isRoot()) + continue; + variable->clearVotes(); + } + + // Identify the set of variables that are always subject to the same structure + // checks. For now, only consider monomorphic structure checks (one structure). + + for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { + BasicBlock* block = m_graph.m_blocks[blockIndex].get(); + if (!block) + continue; + for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { + NodeIndex nodeIndex = block->at(indexInBlock); + Node& node = m_graph[nodeIndex]; + if (!node.shouldGenerate()) + continue; + switch (node.op()) { + case CheckStructure: { + Node& child = m_graph[node.child1()]; + if (child.op() != GetLocal) + break; + VariableAccessData* variable = child.variableAccessData(); + variable->vote(VoteStructureCheck); + if (variable->isCaptured() || variable->structureCheckHoistingFailed()) + break; + if (!isCellSpeculation(variable->prediction())) + break; + noticeStructureCheck(variable, node.structureSet()); + break; + } + + case ForwardCheckStructure: + // We currently rely on the fact that we're the only ones who would + // insert this node. + ASSERT_NOT_REACHED(); + break; + + case GetByOffset: + case PutByOffset: + case PutStructure: + case StructureTransitionWatchpoint: + case AllocatePropertyStorage: + case ReallocatePropertyStorage: + case GetPropertyStorage: + // Don't count these uses. + break; + + default: + m_graph.vote(node, VoteOther); + break; + } + } + } + + // Disable structure hoisting on variables that appear to mostly be used in + // contexts where it doesn't make sense. + + for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { + VariableAccessData* variable = &m_graph.m_variableAccessData[i]; + if (!variable->isRoot()) + continue; + if (variable->voteRatio() >= Options::structureCheckVoteRatioForHoisting()) + continue; + HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); + if (iter == m_map.end()) + continue; +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + dataLog("Zeroing the structure to hoist for %s because the ratio is %lf.\n", + m_graph.nameOfVariableAccessData(variable), variable->voteRatio()); +#endif + iter->second.m_structure = 0; + } + + // Identify the set of variables that are live across a structure clobber. + + Operands<VariableAccessData*> live( + m_graph.m_blocks[0]->variablesAtTail.numberOfArguments(), + m_graph.m_blocks[0]->variablesAtTail.numberOfLocals()); + for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { + BasicBlock* block = m_graph.m_blocks[blockIndex].get(); + if (!block) + continue; + ASSERT(live.numberOfArguments() == block->variablesAtTail.numberOfArguments()); + ASSERT(live.numberOfLocals() == block->variablesAtTail.numberOfLocals()); + for (unsigned i = live.size(); i--;) { + NodeIndex indexAtTail = block->variablesAtTail[i]; + VariableAccessData* variable; + if (indexAtTail == NoNode) + variable = 0; + else + variable = m_graph[indexAtTail].variableAccessData(); + live[i] = variable; + } + for (unsigned indexInBlock = block->size(); indexInBlock--;) { + NodeIndex nodeIndex = block->at(indexInBlock); + Node& node = m_graph[nodeIndex]; + if (!node.shouldGenerate()) + continue; + switch (node.op()) { + case GetLocal: + case Flush: + // This is a birth. + live.operand(node.local()) = node.variableAccessData(); + break; + + case SetLocal: + case SetArgument: + ASSERT(live.operand(node.local())); // Must be live. + ASSERT(live.operand(node.local()) == node.variableAccessData()); // Must have the variable we expected. + // This is a death. + live.operand(node.local()) = 0; + break; + + // Use the CFA's notion of what clobbers the world. + case ValueAdd: + if (m_graph.addShouldSpeculateInteger(node)) + break; + if (Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) + break; + clobber(live); + break; + + case CompareLess: + case CompareLessEq: + case CompareGreater: + case CompareGreaterEq: + case CompareEq: { + Node& left = m_graph[node.child1()]; + Node& right = m_graph[node.child2()]; + if (Node::shouldSpeculateInteger(left, right)) + break; + if (Node::shouldSpeculateNumber(left, right)) + break; + if (node.op() == CompareEq) { + if ((m_graph.isConstant(node.child1().index()) + && m_graph.valueOfJSConstant(node.child1().index()).isNull()) + || (m_graph.isConstant(node.child2().index()) + && m_graph.valueOfJSConstant(node.child2().index()).isNull())) + break; + + if (Node::shouldSpeculateFinalObject(left, right)) + break; + if (Node::shouldSpeculateArray(left, right)) + break; + if (left.shouldSpeculateFinalObject() && right.shouldSpeculateFinalObjectOrOther()) + break; + if (right.shouldSpeculateFinalObject() && left.shouldSpeculateFinalObjectOrOther()) + break; + if (left.shouldSpeculateArray() && right.shouldSpeculateArrayOrOther()) + break; + if (right.shouldSpeculateArray() && left.shouldSpeculateArrayOrOther()) + break; + } + clobber(live); + break; + } + + case GetByVal: + if (!node.prediction() || !m_graph[node.child1()].prediction() || !m_graph[node.child2()].prediction()) + break; + if (!isActionableArraySpeculation(m_graph[node.child1()].prediction()) || !m_graph[node.child2()].shouldSpeculateInteger()) + clobber(live); + break; + + case PutByVal: + case PutByValAlias: { + Edge child1 = m_graph.varArgChild(node, 0); + Edge child2 = m_graph.varArgChild(node, 1); + + if (!m_graph[child1].prediction() || !m_graph[child2].prediction()) + break; + if (!m_graph[child2].shouldSpeculateInteger() || !isActionableMutableArraySpeculation(m_graph[child1].prediction())) { + clobber(live); + break; + } + if (node.op() == PutByValAlias) + break; + if (m_graph[child1].shouldSpeculateArguments()) + break; + if (m_graph[child1].shouldSpeculateInt8Array()) + break; + if (m_graph[child1].shouldSpeculateInt16Array()) + break; + if (m_graph[child1].shouldSpeculateInt32Array()) + break; + if (m_graph[child1].shouldSpeculateUint8Array()) + break; + if (m_graph[child1].shouldSpeculateUint8ClampedArray()) + break; + if (m_graph[child1].shouldSpeculateUint16Array()) + break; + if (m_graph[child1].shouldSpeculateUint32Array()) + break; + if (m_graph[child1].shouldSpeculateFloat32Array()) + break; + if (m_graph[child1].shouldSpeculateFloat64Array()) + break; + clobber(live); + break; + } + + case GetMyArgumentsLengthSafe: + case GetMyArgumentByValSafe: + case GetById: + case GetByIdFlush: + case PutStructure: + case PhantomPutStructure: + case PutById: + case PutByIdDirect: + case Call: + case Construct: + case Resolve: + case ResolveBase: + case ResolveBaseStrictPut: + case ResolveGlobal: + clobber(live); + break; + + default: + ASSERT(node.op() != Phi); + break; + } + } + } + + bool changed = false; + +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin(); + it != m_map.end(); ++it) { + if (!it->second.m_structure) { + dataLog("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->first)); + continue; + } + if (it->second.m_isClobbered && !it->second.m_structure->transitionWatchpointSetIsStillValid()) { + dataLog("Not hoisting checks for %s because the structure is clobbered and has an invalid watchpoint set.\n", m_graph.nameOfVariableAccessData(it->first)); + continue; + } + dataLog("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->first)); + } +#endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + + // Make changes: + // 1) If a variable's live range does not span a clobber, then inject structure + // checks before the SetLocal. + // 2) If a variable's live range spans a clobber but is watchpointable, then + // inject structure checks before the SetLocal and replace all other structure + // checks on that variable with structure transition watchpoints. + + InsertionSet<NodeIndex> insertionSet; + for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { + BasicBlock* block = m_graph.m_blocks[blockIndex].get(); + if (!block) + continue; + for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { + NodeIndex nodeIndex = block->at(indexInBlock); + Node& node = m_graph[nodeIndex]; + // Be careful not to use 'node' after appending to the graph. In those switch + // cases where we need to append, we first carefully extract everything we need + // from the node, before doing any appending. + if (!node.shouldGenerate()) + continue; + switch (node.op()) { + case SetArgument: { + ASSERT(!blockIndex); + // Insert a GetLocal and a CheckStructure immediately following this + // SetArgument, if the variable was a candidate for structure hoisting. + // If the basic block previously only had the SetArgument as its + // variable-at-tail, then replace it with this GetLocal. + VariableAccessData* variable = node.variableAccessData(); + HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); + if (iter == m_map.end()) + break; + if (!iter->second.m_structure) + break; + if (iter->second.m_isClobbered && !iter->second.m_structure->transitionWatchpointSetIsStillValid()) + break; + + node.ref(); + + CodeOrigin codeOrigin = node.codeOrigin; + + Node getLocal(GetLocal, codeOrigin, OpInfo(variable), nodeIndex); + getLocal.predict(variable->prediction()); + getLocal.ref(); + NodeIndex getLocalIndex = m_graph.size(); + m_graph.append(getLocal); + insertionSet.append(indexInBlock + 1, getLocalIndex); + + Node checkStructure(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->second.m_structure)), getLocalIndex); + checkStructure.ref(); + NodeIndex checkStructureIndex = m_graph.size(); + m_graph.append(checkStructure); + insertionSet.append(indexInBlock + 1, checkStructureIndex); + + if (block->variablesAtTail.operand(variable->local()) == nodeIndex) + block->variablesAtTail.operand(variable->local()) = getLocalIndex; + + changed = true; + break; + } + + case SetLocal: { + VariableAccessData* variable = node.variableAccessData(); + HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); + if (iter == m_map.end()) + break; + if (!iter->second.m_structure) + break; + if (iter->second.m_isClobbered && !iter->second.m_structure->transitionWatchpointSetIsStillValid()) + break; + + // First insert a dead SetLocal to tell OSR that the child's value should + // be dropped into this bytecode variable if the CheckStructure decides + // to exit. + + CodeOrigin codeOrigin = node.codeOrigin; + NodeIndex child1 = node.child1().index(); + + Node setLocal(SetLocal, codeOrigin, OpInfo(variable), child1); + NodeIndex setLocalIndex = m_graph.size(); + m_graph.append(setLocal); + insertionSet.append(indexInBlock, setLocalIndex); + m_graph[child1].ref(); + // Use a ForwardCheckStructure to indicate that we should exit to the + // next bytecode instruction rather than reexecuting the current one. + Node checkStructure(ForwardCheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->second.m_structure)), child1); + checkStructure.ref(); + NodeIndex checkStructureIndex = m_graph.size(); + m_graph.append(checkStructure); + insertionSet.append(indexInBlock, checkStructureIndex); + changed = true; + break; + } + + case CheckStructure: { + Node& child = m_graph[node.child1()]; + if (child.op() != GetLocal) + break; + HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(child.variableAccessData()); + if (iter == m_map.end()) + break; + if (!iter->second.m_structure) + break; + if (!iter->second.m_isClobbered) { + node.setOpAndDefaultFlags(Phantom); + ASSERT(node.refCount() == 1); + break; + } + if (!iter->second.m_structure->transitionWatchpointSetIsStillValid()) + break; + ASSERT(iter->second.m_structure == node.structureSet().singletonStructure()); + node.convertToStructureTransitionWatchpoint(); + changed = true; + break; + } + + default: + break; + } + } + insertionSet.execute(*block); + } + + return changed; + } + +private: + void noticeStructureCheck(VariableAccessData* variable, Structure* structure) + { + HashMap<VariableAccessData*, CheckData>::AddResult result = + m_map.add(variable, CheckData(structure, false)); + if (result.isNewEntry) + return; + if (result.iterator->second.m_structure == structure) + return; + result.iterator->second.m_structure = 0; + } + + void noticeStructureCheck(VariableAccessData* variable, const StructureSet& set) + { + if (set.size() != 1) { + noticeStructureCheck(variable, 0); + return; + } + noticeStructureCheck(variable, set.singletonStructure()); + } + + void noticeClobber(VariableAccessData* variable) + { + HashMap<VariableAccessData*, CheckData>::iterator iter = + m_map.find(variable); + if (iter == m_map.end()) + return; + iter->second.m_isClobbered = true; + } + + void clobber(const Operands<VariableAccessData*>& live) + { + for (size_t i = live.size(); i--;) { + VariableAccessData* variable = live[i]; + if (!variable) + continue; + noticeClobber(variable); + } + } + + struct CheckData { + Structure* m_structure; + bool m_isClobbered; + + CheckData() + : m_structure(0) + , m_isClobbered(false) + { + } + + CheckData(Structure* structure, bool isClobbered) + : m_structure(structure) + , m_isClobbered(isClobbered) + { + } + }; + + HashMap<VariableAccessData*, CheckData> m_map; +}; + +bool performStructureCheckHoisting(Graph& graph) +{ + SamplingRegion samplingRegion("DFG Structure Check Hoisting Phase"); + return runPhase<StructureCheckHoistingPhase>(graph); +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + + diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.h b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.h new file mode 100644 index 000000000..1e6462a92 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 Apple Inc. 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 DFGStructureCheckHoistingPhase_h +#define DFGStructureCheckHoistingPhase_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +namespace JSC { namespace DFG { + +class Graph; + +// Hoists CheckStructure on variables to assignments to those variables, if either of +// the following is true: +// A) The structure's transition watchpoint set is valid. +// B) The span of code within which the variable is live has no effects that might +// clobber the structure. + +bool performStructureCheckHoisting(Graph&); + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGStructureCheckHoistingPhase_h + diff --git a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h index e734e6387..6d8e89799 100644 --- a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h +++ b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h @@ -37,10 +37,10 @@ namespace JSC { namespace DFG { +enum DoubleBallot { VoteValue, VoteDouble }; + class VariableAccessData : public UnionFind<VariableAccessData> { public: - enum Ballot { VoteValue, VoteDouble }; - VariableAccessData() : m_local(static_cast<VirtualRegister>(std::numeric_limits<int>::min())) , m_prediction(SpecNone) @@ -49,6 +49,7 @@ public: , m_doubleFormatState(EmptyDoubleFormatState) , m_isCaptured(false) , m_isArgumentsAlias(false) + , m_structureCheckHoistingFailed(false) { clearVotes(); } @@ -61,6 +62,7 @@ public: , m_doubleFormatState(EmptyDoubleFormatState) , m_isCaptured(isCaptured) , m_isArgumentsAlias(false) + , m_structureCheckHoistingFailed(false) { clearVotes(); } @@ -90,6 +92,20 @@ public: return m_isCaptured; } + bool mergeStructureCheckHoistingFailed(bool failed) + { + bool newFailed = m_structureCheckHoistingFailed | failed; + if (newFailed == m_structureCheckHoistingFailed) + return false; + m_structureCheckHoistingFailed = newFailed; + return true; + } + + bool structureCheckHoistingFailed() + { + return m_structureCheckHoistingFailed; + } + bool mergeIsArgumentsAlias(bool isArgumentsAlias) { bool newIsArgumentsAlias = m_isArgumentsAlias | isArgumentsAlias; @@ -136,20 +152,20 @@ public: void clearVotes() { ASSERT(find() == this); - m_votes[VoteValue] = 0; - m_votes[VoteDouble] = 0; + m_votes[0] = 0; + m_votes[1] = 0; } - void vote(Ballot ballot) + void vote(unsigned ballot) { - ASSERT(static_cast<unsigned>(ballot) < 2); + ASSERT(ballot < 2); m_votes[ballot]++; } - double doubleVoteRatio() + double voteRatio() { ASSERT(find() == this); - return static_cast<double>(m_votes[VoteDouble]) / m_votes[VoteValue]; + return static_cast<double>(m_votes[1]) / m_votes[0]; } bool shouldUseDoubleFormatAccordingToVote() @@ -176,7 +192,7 @@ public: // If the variable has been voted to become a double, then make it a // double. - if (doubleVoteRatio() >= Options::doubleVoteRatioForDoubleFormat()) + if (voteRatio() >= Options::doubleVoteRatioForDoubleFormat()) return true; return false; @@ -250,11 +266,12 @@ private: SpeculatedType m_argumentAwarePrediction; NodeFlags m_flags; - float m_votes[2]; + float m_votes[2]; // Used primarily for double voting but may be reused for other purposes. DoubleFormatState m_doubleFormatState; bool m_isCaptured; bool m_isArgumentsAlias; + bool m_structureCheckHoistingFailed; }; } } // namespace JSC::DFG |