diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg')
33 files changed, 1726 insertions, 706 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index ee0cc9ab7..7ab05f329 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -54,13 +54,7 @@ AbstractState::AbstractState(Graph& graph) , m_variables(m_codeBlock->numParameters(), graph.m_localVars) , m_block(0) { - size_t maxBlockSize = 0; - for (size_t i = 0; i < graph.m_blocks.size(); ++i) { - BasicBlock* block = graph.m_blocks[i].get(); - if (block->end - block->begin > maxBlockSize) - maxBlockSize = block->end - block->begin; - } - m_nodes.resize(maxBlockSize); + m_nodes.resize(graph.size()); } AbstractState::~AbstractState() { } @@ -75,8 +69,9 @@ void AbstractState::beginBasicBlock(BasicBlock* basicBlock) ASSERT(basicBlock->variablesAtTail.numberOfLocals() == basicBlock->valuesAtTail.numberOfLocals()); ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->variablesAtTail.numberOfLocals()); - for (size_t i = 0; i < basicBlock->end - basicBlock->begin; ++i) - m_nodes[i].clear(); + for (size_t i = 0; i < basicBlock->size(); i++) + m_nodes[basicBlock->at(i)].clear(); + m_variables = basicBlock->valuesAtHead; m_haveStructures = false; for (size_t i = 0; i < m_variables.numberOfArguments(); ++i) { @@ -113,6 +108,11 @@ void AbstractState::initialize(Graph& graph) continue; } + if (graph.argumentIsCaptured(i)) { + root->valuesAtHead.argument(i).makeTop(); + continue; + } + PredictedType prediction = node.variableAccessData()->prediction(); if (isInt32Prediction(prediction)) root->valuesAtHead.argument(i).set(PredictInt32); @@ -143,6 +143,11 @@ void AbstractState::initialize(Graph& graph) else root->valuesAtHead.argument(i).makeTop(); } + for (size_t i = 0; i < root->valuesAtHead.numberOfLocals(); ++i) { + if (!graph.localIsCaptured(i)) + continue; + root->valuesAtHead.local(i).makeTop(); + } } bool AbstractState::endBasicBlock(MergeMode mergeMode) @@ -164,14 +169,28 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode) #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" Merging state for argument %zu.\n", argument); #endif - changed |= mergeStateAtTail(block->valuesAtTail.argument(argument), m_variables.argument(argument), block->variablesAtTail.argument(argument)); + AbstractValue& destination = block->valuesAtTail.argument(argument); + if (m_graph.argumentIsCaptured(argument)) { + if (!destination.isTop()) { + destination.makeTop(); + changed = true; + } + } else + changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument)); } for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" Merging state for local %zu.\n", local); #endif - changed |= mergeStateAtTail(block->valuesAtTail.local(local), m_variables.local(local), block->variablesAtTail.local(local)); + AbstractValue& destination = block->valuesAtTail.local(local); + if (m_graph.localIsCaptured(local)) { + if (!destination.isTop()) { + destination.makeTop(); + changed = true; + } + } else + changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local)); } } @@ -191,12 +210,13 @@ void AbstractState::reset() m_isValid = false; } -bool AbstractState::execute(NodeIndex nodeIndex) +bool AbstractState::execute(unsigned indexInBlock) { PROFILE(FLAG_FOR_EXECUTION); ASSERT(m_block); ASSERT(m_isValid); + NodeIndex nodeIndex = m_block->at(indexInBlock); Node& node = m_graph[nodeIndex]; if (!node.shouldGenerate()) @@ -216,11 +236,17 @@ bool AbstractState::execute(NodeIndex nodeIndex) } case GetLocal: { - forNode(nodeIndex) = m_variables.operand(node.local()); + if (m_graph.isCaptured(node.local())) + forNode(nodeIndex).makeTop(); + else + forNode(nodeIndex) = m_variables.operand(node.local()); break; } case SetLocal: { + if (m_graph.isCaptured(node.local())) + break; + if (node.variableAccessData()->shouldUseDoubleFormat()) { forNode(node.child1()).filter(PredictNumber); m_variables.operand(node.local()).set(PredictDouble); @@ -265,12 +291,12 @@ bool AbstractState::execute(NodeIndex nodeIndex) break; case ValueToInt32: - if (!m_graph[node.child1()].shouldNotSpeculateInteger()) { + if (m_graph[node.child1()].shouldNotSpeculateInteger()) { if (m_graph[node.child1()].shouldSpeculateDouble()) forNode(node.child1()).filter(PredictNumber); - else - forNode(node.child1()).filter(PredictInt32); - } + } else + forNode(node.child1()).filter(PredictInt32); + forNode(nodeIndex).set(PredictInt32); break; @@ -289,7 +315,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) break; } if (node.op == ValueAdd) { - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); forNode(nodeIndex).set(PredictString | PredictInt32 | PredictNumber); break; } @@ -311,6 +337,17 @@ bool AbstractState::execute(NodeIndex nodeIndex) break; } + case ArithNegate: { + if (m_graph.negateShouldSpeculateInteger(node)) { + forNode(node.child1()).filter(PredictInt32); + forNode(nodeIndex).set(PredictInt32); + break; + } + forNode(node.child1()).filter(PredictNumber); + forNode(nodeIndex).set(PredictDouble); + break; + } + case ArithMul: case ArithDiv: case ArithMin: @@ -368,7 +405,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) else if (child.shouldSpeculateNumber()) forNode(node.child1()).filter(PredictNumber); else - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); forNode(nodeIndex).set(PredictBoolean); break; } @@ -391,7 +428,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) filter = PredictArray; else { filter = PredictTop; - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); } forNode(node.child1()).filter(filter); forNode(node.child2()).filter(filter); @@ -421,7 +458,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) break; } if (!isActionableArrayPrediction(m_graph[node.child1()].prediction()) || !m_graph[node.child2()].shouldSpeculateInteger()) { - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); forNode(nodeIndex).makeTop(); break; } @@ -507,7 +544,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) } if (!m_graph[node.child2()].shouldSpeculateInteger() || !isActionableMutableArrayPrediction(m_graph[node.child1()].prediction())) { ASSERT(node.op == PutByVal); - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); forNode(nodeIndex).makeTop(); break; } @@ -693,10 +730,25 @@ bool AbstractState::execute(NodeIndex nodeIndex) } case NewObject: - forNode(nodeIndex).set(m_codeBlock->globalObject()->emptyObjectStructure()); + forNode(nodeIndex).set(m_codeBlock->globalObjectFor(node.codeOrigin)->emptyObjectStructure()); m_haveStructures = true; break; - + + case CreateActivation: + forNode(nodeIndex).set(m_graph.m_globalData.activationStructure.get()); + m_haveStructures = true; + break; + + case TearOffActivation: + // Does nothing that is user-visible. + break; + + case NewFunction: + case NewFunctionExpression: + case NewFunctionNoCheck: + forNode(nodeIndex).set(m_codeBlock->globalObjectFor(node.codeOrigin)->functionStructure()); + break; + case GetCallee: forNode(nodeIndex).set(PredictFunction); break; @@ -710,7 +762,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) break; case PutScopedVar: - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); break; case GetById: @@ -721,7 +773,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) } if (isCellPrediction(m_graph[node.child1()].prediction())) forNode(node.child1()).filter(PredictCell); - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); forNode(nodeIndex).makeTop(); break; @@ -783,7 +835,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) break; case PutStructure: - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); forNode(node.child1()).set(node.structureTransitionData().newStructure); m_haveStructures = true; break; @@ -874,7 +926,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) case PutById: case PutByIdDirect: forNode(node.child1()).filter(PredictCell); - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); break; case GetGlobalVar: @@ -910,7 +962,7 @@ bool AbstractState::execute(NodeIndex nodeIndex) case ResolveBase: case ResolveBaseStrictPut: case ResolveGlobal: - clobberStructures(nodeIndex); + clobberStructures(indexInBlock); forNode(nodeIndex).makeTop(); break; @@ -927,13 +979,13 @@ bool AbstractState::execute(NodeIndex nodeIndex) return m_isValid; } -inline void AbstractState::clobberStructures(NodeIndex nodeIndex) +inline void AbstractState::clobberStructures(unsigned indexInBlock) { PROFILE(FLAG_FOR_STRUCTURE_CLOBBERING); if (!m_haveStructures) return; - for (size_t i = nodeIndex - m_block->begin + 1; i-- > 0;) - m_nodes[i].clobberStructures(); + for (size_t i = indexInBlock + 1; i--;) + forNode(m_block->at(i)).clobberStructures(); for (size_t i = 0; i < m_variables.numberOfArguments(); ++i) m_variables.argument(i).clobberStructures(); for (size_t i = 0; i < m_variables.numberOfLocals(); ++i) @@ -946,7 +998,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract if (nodeIndex == NoNode) return false; - AbstractValue* source; + AbstractValue source; Node& node = m_graph[nodeIndex]; if (!node.refCount()) @@ -961,7 +1013,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract case SetArgument: case Flush: // The block transfers the value from head to tail. - source = &inVariable; + source = inVariable; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" Transfering from head to tail.\n"); #endif @@ -969,7 +1021,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract case GetLocal: // The block refines the value with additional speculations. - source = &forNode(nodeIndex); + source = forNode(nodeIndex); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" Refining.\n"); #endif @@ -978,7 +1030,10 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract case SetLocal: // The block sets the variable, and potentially refines it, both // before and after setting it. - source = &forNode(node.child1()); + if (node.variableAccessData()->shouldUseDoubleFormat()) + source.set(PredictDouble); + else + source = forNode(node.child1()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" Setting.\n"); #endif @@ -986,11 +1041,10 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract default: ASSERT_NOT_REACHED(); - source = 0; break; } - if (destination == *source) { + if (destination == source) { // Abstract execution did not change the output value of the variable, for this // basic block, on this iteration. #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) @@ -1002,7 +1056,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract // Abstract execution reached a new conclusion about the speculations reached about // this variable after execution of this basic block. Update the state, and return // true to indicate that the fixpoint must go on! - destination = *source; + destination = source; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" Changed!\n"); #endif @@ -1016,11 +1070,29 @@ inline bool AbstractState::merge(BasicBlock* from, BasicBlock* to) bool changed = false; - for (size_t argument = 0; argument < from->variablesAtTail.numberOfArguments(); ++argument) - changed |= mergeVariableBetweenBlocks(to->valuesAtHead.argument(argument), from->valuesAtTail.argument(argument), to->variablesAtHead.argument(argument), from->variablesAtTail.argument(argument)); + for (size_t argument = 0; argument < from->variablesAtTail.numberOfArguments(); ++argument) { + AbstractValue& destination = to->valuesAtHead.argument(argument); + if (m_graph.argumentIsCaptured(argument)) { + if (destination.isTop()) + continue; + destination.makeTop(); + changed = true; + continue; + } + changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.argument(argument), to->variablesAtHead.argument(argument), from->variablesAtTail.argument(argument)); + } - for (size_t local = 0; local < from->variablesAtTail.numberOfLocals(); ++local) - changed |= mergeVariableBetweenBlocks(to->valuesAtHead.local(local), from->valuesAtTail.local(local), to->variablesAtHead.local(local), from->variablesAtTail.local(local)); + for (size_t local = 0; local < from->variablesAtTail.numberOfLocals(); ++local) { + AbstractValue& destination = to->valuesAtHead.local(local); + if (m_graph.localIsCaptured(local)) { + if (destination.isTop()) + continue; + destination.makeTop(); + changed = true; + continue; + } + changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.local(local), to->variablesAtHead.local(local), from->variablesAtTail.local(local)); + } if (!to->cfaHasVisited) changed = true; @@ -1034,7 +1106,7 @@ inline bool AbstractState::mergeToSuccessors(Graph& graph, BasicBlock* basicBloc { PROFILE(FLAG_FOR_MERGE_TO_SUCCESSORS); - Node& terminal = graph[basicBlock->end - 1]; + Node& terminal = graph[basicBlock->last()]; ASSERT(terminal.isTerminal()); @@ -1073,15 +1145,17 @@ inline bool AbstractState::mergeVariableBetweenBlocks(AbstractValue& destination void AbstractState::dump(FILE* out) { bool first = true; - for (size_t i = 0; i < m_nodes.size(); ++i) { - if (m_nodes[i].isClear()) + for (size_t i = 0; i < m_block->size(); ++i) { + NodeIndex index = m_block->at(i); + AbstractValue& value = m_nodes[index]; + if (value.isClear()) continue; if (first) first = false; else fprintf(out, " "); - fprintf(out, "@%lu:", static_cast<unsigned long>(i + m_block->begin)); - m_nodes[i].dump(out); + fprintf(out, "@%lu:", static_cast<unsigned long>(index)); + value.dump(out); } } #endif diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.h b/Source/JavaScriptCore/dfg/DFGAbstractState.h index 256e7495f..d9d5cc0f8 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.h @@ -98,7 +98,7 @@ public: AbstractValue& forNode(NodeIndex nodeIndex) { - return m_nodes[nodeIndex - m_block->begin]; + return m_nodes[nodeIndex]; } AbstractValue& forNode(NodeUse nodeUse) @@ -152,7 +152,7 @@ public: // if execution should continue past this node. Notably, it will return true // for block terminals, so long as those terminals are not Return or variants // of Throw. - bool execute(NodeIndex); + bool execute(unsigned); // Is the execution state still valid? This will be false if execute() has // returned false previously. @@ -163,20 +163,20 @@ public: // that block must be abstractly interpreted again. This also sets // to->cfaShouldRevisit to true, if it returns true, or if to has not been // visited yet. - static bool merge(BasicBlock* from, BasicBlock* to); + bool merge(BasicBlock* from, BasicBlock* to); // Merge the abstract state stored at the block's tail into all of its // successors. Returns true if any of the successors' states changed. Note // that this is automatically called in endBasicBlock() if MergeMode is // MergeToSuccessors. - static bool mergeToSuccessors(Graph&, BasicBlock*); + bool mergeToSuccessors(Graph&, BasicBlock*); #ifndef NDEBUG void dump(FILE* out); #endif private: - void clobberStructures(NodeIndex); + void clobberStructures(unsigned); bool mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, NodeIndex); @@ -185,7 +185,7 @@ private: CodeBlock* m_codeBlock; Graph& m_graph; - Vector<AbstractValue, 32> m_nodes; + Vector<AbstractValue, 64> m_nodes; Operands<AbstractValue> m_variables; BasicBlock* m_block; bool m_haveStructures; diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h index aa5518187..682c7a90f 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h @@ -442,6 +442,11 @@ struct AbstractValue { if (mergePredictions(m_type, predictionFromValue(value)) != m_type) return false; + if (value.isEmpty()) { + ASSERT(m_type & PredictEmpty); + return true; + } + if (m_structure.isTop()) return true; diff --git a/Source/JavaScriptCore/dfg/DFGArithNodeFlagsInferencePhase.cpp b/Source/JavaScriptCore/dfg/DFGArithNodeFlagsInferencePhase.cpp index f55533a61..9a49364dd 100644 --- a/Source/JavaScriptCore/dfg/DFGArithNodeFlagsInferencePhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGArithNodeFlagsInferencePhase.cpp @@ -80,11 +80,8 @@ private: if (!node.shouldGenerate()) return; - NodeType op = node.op; - ArithNodeFlags flags = 0; - - if (node.hasArithNodeFlags()) - flags = node.rawArithNodeFlags(); + NodeType op = static_cast<NodeType>(node.op); + NodeFlags flags = node.flags; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" %s @%u: %s ", Graph::opName(op), m_compileIndex, arithNodeFlagsAsString(flags)); @@ -131,6 +128,11 @@ private: break; } + case ArithNegate: { + changed |= m_graph[node.child1()].mergeArithNodeFlags(flags); + break; + } + case ArithMul: case ArithDiv: { // As soon as a multiply happens, we can easily end up in the part @@ -173,7 +175,7 @@ private: default: flags |= NodeUsedAsNumber | NodeNeedsNegZero; - if (op & NodeHasVarArgs) { + if (node.flags & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeArithNodeFlags(flags); } else { @@ -217,6 +219,9 @@ private: NodeIndex m_compileIndex; bool m_changed; +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + unsigned m_count; +#endif }; void performArithNodeFlagsInference(Graph& graph) diff --git a/Source/JavaScriptCore/dfg/DFGBasicBlock.h b/Source/JavaScriptCore/dfg/DFGBasicBlock.h index 9d464bdc2..1c890b498 100644 --- a/Source/JavaScriptCore/dfg/DFGBasicBlock.h +++ b/Source/JavaScriptCore/dfg/DFGBasicBlock.h @@ -38,11 +38,9 @@ namespace JSC { namespace DFG { typedef Vector <BlockIndex, 2> PredecessorList; -struct BasicBlock { - BasicBlock(unsigned bytecodeBegin, NodeIndex begin, unsigned numArguments, unsigned numLocals) +struct BasicBlock : Vector<NodeIndex, 8> { + BasicBlock(unsigned bytecodeBegin, unsigned numArguments, unsigned numLocals) : bytecodeBegin(bytecodeBegin) - , begin(begin) - , end(NoNode) , isOSRTarget(false) , cfaHasVisited(false) , cfaShouldRevisit(false) @@ -69,8 +67,6 @@ struct BasicBlock { // for other purposes due to inlining. unsigned bytecodeBegin; - NodeIndex begin; - NodeIndex end; bool isOSRTarget; bool cfaHasVisited; bool cfaShouldRevisit; @@ -79,6 +75,7 @@ struct BasicBlock { #endif bool isReachable; + Vector<NodeIndex> phis; PredecessorList m_predecessors; Operands<NodeIndex, NodeIndexTraits> variablesAtHead; diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeCache.h b/Source/JavaScriptCore/dfg/DFGByteCodeCache.h index fd3b5147f..f6a745c66 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeCache.h +++ b/Source/JavaScriptCore/dfg/DFGByteCodeCache.h @@ -138,7 +138,6 @@ public: delete iter->second.codeBlock; continue; } - iter->second.codeBlock->m_shouldDiscardBytecode = iter->second.oldValueOfShouldDiscardBytecode; } } @@ -155,7 +154,6 @@ public: value.codeBlock = key.executable()->codeBlockWithBytecodeFor(key.kind()); if (value.codeBlock) { value.owned = false; - value.oldValueOfShouldDiscardBytecode = value.codeBlock->m_shouldDiscardBytecode; } else { // Nope, so try to parse one. JSObject* exception; @@ -171,13 +169,6 @@ public: value.codeBlock = 0; } - // If we're about to return a code block, make sure that we're not going - // to be discarding its bytecode if a GC were to happen during DFG - // compilation. That's unlikely, but it's good to thoroughly enjoy this - // kind of paranoia. - if (!!value.codeBlock) - value.codeBlock->m_shouldDiscardBytecode = false; - m_map.add(key, value); return value.codeBlock; diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index 0e575db4e..3a3678d12 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -66,6 +66,7 @@ public: , m_globalResolveNumber(0) , m_inlineStackTop(0) , m_haveBuiltOperandMaps(false) + , m_emptyJSValueIndex(UINT_MAX) { ASSERT(m_profiledBlock); @@ -185,16 +186,32 @@ private: // Two possibilities: either the block wants the local to be live // but has not loaded its value, or it has loaded its value, in // which case we're done. - Node& flushChild = m_graph[nodePtr->child1()]; + nodeIndex = nodePtr->child1().index(); + Node& flushChild = m_graph[nodeIndex]; if (flushChild.op == Phi) { VariableAccessData* variableAccessData = flushChild.variableAccessData(); - nodeIndex = injectLazyOperandPrediction(addToGraph(GetLocal, OpInfo(variableAccessData), nodePtr->child1().index())); + nodeIndex = injectLazyOperandPrediction(addToGraph(GetLocal, OpInfo(variableAccessData), nodeIndex)); m_currentBlock->variablesAtTail.local(operand) = nodeIndex; return nodeIndex; } nodePtr = &flushChild; } + + ASSERT(&m_graph[nodeIndex] == nodePtr); ASSERT(nodePtr->op != Flush); + + if (m_graph.localIsCaptured(operand)) { + // We wish to use the same variable access data as the previous access, + // but for all other purposes we want to issue a load since for all we + // know, at this stage of compilation, the local has been clobbered. + + // Make sure we link to the Phi node, not to the GetLocal. + if (nodePtr->op == GetLocal) + nodeIndex = nodePtr->child1().index(); + + return injectLazyOperandPrediction(addToGraph(GetLocal, OpInfo(nodePtr->variableAccessData()), nodeIndex)); + } + if (nodePtr->op == GetLocal) return nodeIndex; ASSERT(nodePtr->op == SetLocal); @@ -218,7 +235,11 @@ private: } void setLocal(unsigned operand, NodeIndex value) { - m_currentBlock->variablesAtTail.local(operand) = addToGraph(SetLocal, OpInfo(newVariableAccessData(operand)), value); + VariableAccessData* variableAccessData = newVariableAccessData(operand); + NodeIndex nodeIndex = addToGraph(SetLocal, OpInfo(variableAccessData), value); + m_currentBlock->variablesAtTail.local(operand) = nodeIndex; + if (m_graph.localIsCaptured(operand)) + addToGraph(Flush, OpInfo(variableAccessData), nodeIndex); } // Used in implementing get/set, above, where the operand is an argument. @@ -226,7 +247,7 @@ private: { unsigned argument = operandToArgument(operand); ASSERT(argument < m_numArguments); - + NodeIndex nodeIndex = m_currentBlock->variablesAtTail.argument(argument); if (nodeIndex != NoNode) { @@ -235,16 +256,18 @@ private: // Two possibilities: either the block wants the local to be live // but has not loaded its value, or it has loaded its value, in // which case we're done. - Node& flushChild = m_graph[nodePtr->child1()]; + nodeIndex = nodePtr->child1().index(); + Node& flushChild = m_graph[nodeIndex]; if (flushChild.op == Phi) { VariableAccessData* variableAccessData = flushChild.variableAccessData(); - nodeIndex = injectLazyOperandPrediction(addToGraph(GetLocal, OpInfo(variableAccessData), nodePtr->child1().index())); + nodeIndex = injectLazyOperandPrediction(addToGraph(GetLocal, OpInfo(variableAccessData), nodeIndex)); m_currentBlock->variablesAtTail.local(operand) = nodeIndex; return nodeIndex; } nodePtr = &flushChild; } + ASSERT(&m_graph[nodeIndex] == nodePtr); ASSERT(nodePtr->op != Flush); if (nodePtr->op == SetArgument) { @@ -256,6 +279,12 @@ private: return nodeIndex; } + if (m_graph.argumentIsCaptured(argument)) { + if (nodePtr->op == GetLocal) + nodeIndex = nodePtr->child1().index(); + return injectLazyOperandPrediction(addToGraph(GetLocal, OpInfo(nodePtr->variableAccessData()), nodeIndex)); + } + if (nodePtr->op == GetLocal) return nodeIndex; @@ -278,11 +307,15 @@ private: { unsigned argument = operandToArgument(operand); ASSERT(argument < m_numArguments); - - m_currentBlock->variablesAtTail.argument(argument) = addToGraph(SetLocal, OpInfo(newVariableAccessData(operand)), value); + + VariableAccessData* variableAccessData = newVariableAccessData(operand); + NodeIndex nodeIndex = addToGraph(SetLocal, OpInfo(variableAccessData), value); + m_currentBlock->variablesAtTail.argument(argument) = nodeIndex; + if (m_graph.argumentIsCaptured(argument)) + addToGraph(Flush, OpInfo(variableAccessData), nodeIndex); } - void flush(int operand) + void flushArgument(int operand) { // FIXME: This should check if the same operand had already been flushed to // some other local variable. @@ -308,7 +341,10 @@ private: nodeIndex = node.child1().index(); ASSERT(m_graph[nodeIndex].op != Flush); - + + // Emit a Flush regardless of whether we already flushed it. + // This gives us guidance to see that the variable also needs to be flushed + // for arguments, even if it already had to be flushed for other reasons. addToGraph(Flush, OpInfo(node.variableAccessData()), nodeIndex); return; } @@ -533,8 +569,10 @@ private: { NodeIndex resultIndex = (NodeIndex)m_graph.size(); m_graph.append(Node(op, currentCodeOrigin(), child1, child2, child3)); + ASSERT(op != Phi); + m_currentBlock->append(resultIndex); - if (op & NodeMustGenerate) + if (defaultFlags(op) & NodeMustGenerate) m_graph.ref(resultIndex); return resultIndex; } @@ -542,8 +580,12 @@ private: { NodeIndex resultIndex = (NodeIndex)m_graph.size(); m_graph.append(Node(op, currentCodeOrigin(), info, child1, child2, child3)); + if (op == Phi) + m_currentBlock->phis.append(resultIndex); + else + m_currentBlock->append(resultIndex); - if (op & NodeMustGenerate) + if (defaultFlags(op) & NodeMustGenerate) m_graph.ref(resultIndex); return resultIndex; } @@ -551,8 +593,10 @@ private: { NodeIndex resultIndex = (NodeIndex)m_graph.size(); m_graph.append(Node(op, currentCodeOrigin(), info1, info2, child1, child2, child3)); + ASSERT(op != Phi); + m_currentBlock->append(resultIndex); - if (op & NodeMustGenerate) + if (defaultFlags(op) & NodeMustGenerate) m_graph.ref(resultIndex); return resultIndex; } @@ -561,13 +605,25 @@ private: { NodeIndex resultIndex = (NodeIndex)m_graph.size(); m_graph.append(Node(Node::VarArg, op, currentCodeOrigin(), info1, info2, m_graph.m_varArgChildren.size() - m_numPassedVarArgs, m_numPassedVarArgs)); + ASSERT(op != Phi); + m_currentBlock->append(resultIndex); m_numPassedVarArgs = 0; - if (op & NodeMustGenerate) + if (defaultFlags(op) & NodeMustGenerate) m_graph.ref(resultIndex); return resultIndex; } + + NodeIndex insertPhiNode(OpInfo info, BasicBlock* block) + { + NodeIndex resultIndex = (NodeIndex)m_graph.size(); + m_graph.append(Node(Phi, currentCodeOrigin(), info)); + block->phis.append(resultIndex); + + return resultIndex; + } + void addVarArgChild(NodeIndex child) { m_graph.m_varArgChildren.append(NodeUse(child)); @@ -643,13 +699,14 @@ private: return nodeIndex; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Making %s @%u safe at bc#%u because slow-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(m_graph[nodeIndex].op), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)); + dataLog("Making %s @%u safe at bc#%u because slow-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(static_cast<NodeType>(m_graph[nodeIndex].op)), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)); #endif switch (m_graph[nodeIndex].op) { case UInt32ToNumber: case ArithAdd: case ArithSub: + case ArithNegate: case ValueAdd: case ArithMod: // for ArithMode "MayOverflow" means we tried to divide by zero, or we saw double. m_graph[nodeIndex].mergeArithNodeFlags(NodeMayOverflow); @@ -695,7 +752,7 @@ private: return nodeIndex; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Making %s @%u safe at bc#%u because special fast-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(m_graph[nodeIndex].op), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->specialFastCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)); + dataLog("Making %s @%u safe at bc#%u because special fast-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(static_cast<NodeType>(m_graph[nodeIndex].op)), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->specialFastCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)); #endif // FIXME: It might be possible to make this more granular. The DFG certainly can @@ -912,6 +969,9 @@ private: IdentifierMap m_identifierMap; // Mapping between values and constant numbers. JSValueMap m_jsValueMap; + // Index of the empty value, or UINT_MAX if there is no mapping. This is a horrible + // work-around for the fact that JSValueMap can't handle "empty" values. + unsigned m_emptyJSValueIndex; // Cache of code blocks that we've generated bytecode for. ByteCodeCache<canInlineFunctionFor> m_codeBlockCache; @@ -1066,7 +1126,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c // FIXME: Don't flush constants! for (int i = 1; i < argumentCountIncludingThis; ++i) - flush(registerOffset + argumentToOperand(i)); + flushArgument(registerOffset + argumentToOperand(i)); int inlineCallFrameStart = m_inlineStackTop->remapOperand(registerOffset) - RegisterFile::CallFrameHeaderSize; @@ -1129,7 +1189,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c // the caller to continue in whatever basic block we're in right now. if (!inlineStackEntry.m_didEarlyReturn && inlineStackEntry.m_didReturn) { BasicBlock* lastBlock = m_graph.m_blocks.last().get(); - ASSERT(lastBlock->begin == lastBlock->end || !m_graph.last().isTerminal()); + ASSERT(lastBlock->isEmpty() || !m_graph.last().isTerminal()); // If we created new blocks then the last block needs linking, but in the // caller. It doesn't need to be linked to, but it needs outgoing links. @@ -1161,7 +1221,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c continue; BasicBlock* block = m_graph.m_blocks[inlineStackEntry.m_unlinkedBlocks[i].m_blockIndex].get(); ASSERT(!block->isLinked); - Node& node = m_graph[block->end - 1]; + Node& node = m_graph[block->last()]; ASSERT(node.op == Jump); ASSERT(node.takenBlockIndex() == NoBlock); node.setTakenBlockIndex(m_graph.m_blocks.size()); @@ -1172,7 +1232,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c } // Need to create a new basic block for the continuation at the caller. - OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(nextOffset, m_graph.size(), m_numArguments, m_numLocals)); + OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(nextOffset, m_numArguments, m_numLocals)); #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("Creating inline epilogue basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); #endif @@ -1207,7 +1267,7 @@ bool ByteCodeParser::handleMinMax(bool usesResult, int resultOperand, NodeType o } if (argumentCountIncludingThis == 3) { // Math.min(x, y) - set(resultOperand, addToGraph(op, OpInfo(NodeUseBottom), get(registerOffset + argumentToOperand(1)), get(registerOffset + argumentToOperand(2)))); + set(resultOperand, addToGraph(op, get(registerOffset + argumentToOperand(1)), get(registerOffset + argumentToOperand(2)))); return true; } @@ -1235,7 +1295,7 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins if (!MacroAssembler::supportsFloatingPointAbs()) return false; - NodeIndex nodeIndex = addToGraph(ArithAbs, OpInfo(NodeUseBottom), get(registerOffset + argumentToOperand(1))); + NodeIndex nodeIndex = addToGraph(ArithAbs, get(registerOffset + argumentToOperand(1))); if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow)) m_graph[nodeIndex].mergeArithNodeFlags(NodeMayOverflow); set(resultOperand, nodeIndex); @@ -1364,7 +1424,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) // logic relies on every bytecode resulting in one or more nodes, which would // be true anyway except for op_loop_hint, which emits a Phantom to force this // to be true. - if (m_currentBlock->begin != m_graph.size()) + if (!m_currentBlock->isEmpty()) addToGraph(Jump, OpInfo(m_currentIndex)); else { #if DFG_ENABLE(DEBUG_VERBOSE) @@ -1502,11 +1562,11 @@ bool ByteCodeParser::parseBlock(unsigned limit) if (valueOfInt32Constant(op2) & 0x1f) result = addToGraph(BitURShift, op1, op2); else - result = makeSafe(addToGraph(UInt32ToNumber, OpInfo(NodeUseBottom), op1)); + result = makeSafe(addToGraph(UInt32ToNumber, op1)); } else { // Cannot optimize at this stage; shift & potentially rebox as a double. result = addToGraph(BitURShift, op1, op2); - result = makeSafe(addToGraph(UInt32ToNumber, OpInfo(NodeUseBottom), result)); + result = makeSafe(addToGraph(UInt32ToNumber, result)); } set(currentInstruction[1].u.operand, result); NEXT_OPCODE(op_urshift); @@ -1517,7 +1577,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) case op_pre_inc: { unsigned srcDst = currentInstruction[1].u.operand; NodeIndex op = get(srcDst); - set(srcDst, makeSafe(addToGraph(ArithAdd, OpInfo(NodeUseBottom), op, one()))); + set(srcDst, makeSafe(addToGraph(ArithAdd, op, one()))); NEXT_OPCODE(op_pre_inc); } @@ -1527,14 +1587,14 @@ bool ByteCodeParser::parseBlock(unsigned limit) ASSERT(result != srcDst); // Required for assumptions we make during OSR. NodeIndex op = get(srcDst); set(result, op); - set(srcDst, makeSafe(addToGraph(ArithAdd, OpInfo(NodeUseBottom), op, one()))); + set(srcDst, makeSafe(addToGraph(ArithAdd, op, one()))); NEXT_OPCODE(op_post_inc); } case op_pre_dec: { unsigned srcDst = currentInstruction[1].u.operand; NodeIndex op = get(srcDst); - set(srcDst, makeSafe(addToGraph(ArithSub, OpInfo(NodeUseBottom), op, one()))); + set(srcDst, makeSafe(addToGraph(ArithSub, op, one()))); NEXT_OPCODE(op_pre_dec); } @@ -1543,7 +1603,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) unsigned srcDst = currentInstruction[2].u.operand; NodeIndex op = get(srcDst); set(result, op); - set(srcDst, makeSafe(addToGraph(ArithSub, OpInfo(NodeUseBottom), op, one()))); + set(srcDst, makeSafe(addToGraph(ArithSub, op, one()))); NEXT_OPCODE(op_post_dec); } @@ -1553,38 +1613,44 @@ bool ByteCodeParser::parseBlock(unsigned limit) NodeIndex op1 = get(currentInstruction[2].u.operand); NodeIndex op2 = get(currentInstruction[3].u.operand); if (m_graph[op1].hasNumberResult() && m_graph[op2].hasNumberResult()) - set(currentInstruction[1].u.operand, makeSafe(addToGraph(ArithAdd, OpInfo(NodeUseBottom), op1, op2))); + set(currentInstruction[1].u.operand, makeSafe(addToGraph(ArithAdd, op1, op2))); else - set(currentInstruction[1].u.operand, makeSafe(addToGraph(ValueAdd, OpInfo(NodeUseBottom), op1, op2))); + set(currentInstruction[1].u.operand, makeSafe(addToGraph(ValueAdd, op1, op2))); NEXT_OPCODE(op_add); } case op_sub: { NodeIndex op1 = get(currentInstruction[2].u.operand); NodeIndex op2 = get(currentInstruction[3].u.operand); - set(currentInstruction[1].u.operand, makeSafe(addToGraph(ArithSub, OpInfo(NodeUseBottom), op1, op2))); + set(currentInstruction[1].u.operand, makeSafe(addToGraph(ArithSub, op1, op2))); NEXT_OPCODE(op_sub); } + case op_negate: { + NodeIndex op1 = get(currentInstruction[2].u.operand); + set(currentInstruction[1].u.operand, makeSafe(addToGraph(ArithNegate, op1))); + NEXT_OPCODE(op_negate); + } + case op_mul: { // Multiply requires that the inputs are not truncated, unfortunately. NodeIndex op1 = get(currentInstruction[2].u.operand); NodeIndex op2 = get(currentInstruction[3].u.operand); - set(currentInstruction[1].u.operand, makeSafe(addToGraph(ArithMul, OpInfo(NodeUseBottom), op1, op2))); + set(currentInstruction[1].u.operand, makeSafe(addToGraph(ArithMul, op1, op2))); NEXT_OPCODE(op_mul); } case op_mod: { NodeIndex op1 = get(currentInstruction[2].u.operand); NodeIndex op2 = get(currentInstruction[3].u.operand); - set(currentInstruction[1].u.operand, makeSafe(addToGraph(ArithMod, OpInfo(NodeUseBottom), op1, op2))); + set(currentInstruction[1].u.operand, makeSafe(addToGraph(ArithMod, op1, op2))); NEXT_OPCODE(op_mod); } case op_div: { NodeIndex op1 = get(currentInstruction[2].u.operand); NodeIndex op2 = get(currentInstruction[3].u.operand); - set(currentInstruction[1].u.operand, makeDivSafe(addToGraph(ArithDiv, OpInfo(NodeUseBottom), op1, op2))); + set(currentInstruction[1].u.operand, makeDivSafe(addToGraph(ArithDiv, op1, op2))); NEXT_OPCODE(op_div); } @@ -2187,6 +2253,42 @@ bool ByteCodeParser::parseBlock(unsigned limit) NEXT_OPCODE(op_loop_hint); } + + case op_init_lazy_reg: { + set(currentInstruction[1].u.operand, getJSConstantForValue(JSValue())); + NEXT_OPCODE(op_init_lazy_reg); + } + + case op_create_activation: { + set(currentInstruction[1].u.operand, addToGraph(CreateActivation, get(currentInstruction[1].u.operand))); + NEXT_OPCODE(op_create_activation); + } + + case op_tear_off_activation: { + // This currently ignores arguments because we don't support them yet. + addToGraph(TearOffActivation, get(currentInstruction[1].u.operand)); + NEXT_OPCODE(op_tear_off_activation); + } + + case op_new_func: { + if (!currentInstruction[3].u.operand) { + set(currentInstruction[1].u.operand, + addToGraph(NewFunctionNoCheck, OpInfo(currentInstruction[2].u.operand))); + } else { + set(currentInstruction[1].u.operand, + addToGraph( + NewFunction, + OpInfo(currentInstruction[2].u.operand), + get(currentInstruction[1].u.operand))); + } + NEXT_OPCODE(op_new_func); + } + + case op_new_func_exp: { + set(currentInstruction[1].u.operand, + addToGraph(NewFunctionExpression, OpInfo(currentInstruction[2].u.operand))); + NEXT_OPCODE(op_new_func_exp); + } default: // Parse failed! This should not happen because the capabilities checker @@ -2231,7 +2333,7 @@ void ByteCodeParser::processPhiStack() dataLog(" Did not find node, adding phi.\n"); #endif - valueInPredecessor = addToGraph(Phi, OpInfo(newVariableAccessData(stackType == ArgumentPhiStack ? argumentToOperand(varNo) : static_cast<int>(varNo)))); + valueInPredecessor = insertPhiNode(OpInfo(newVariableAccessData(stackType == ArgumentPhiStack ? argumentToOperand(varNo) : static_cast<int>(varNo))), predecessorBlock); var = valueInPredecessor; if (stackType == ArgumentPhiStack) predecessorBlock->variablesAtHead.setArgumentFirstTime(varNo, valueInPredecessor); @@ -2255,7 +2357,11 @@ void ByteCodeParser::processPhiStack() dataLog(" Found @%u.\n", valueInPredecessor); #endif } - ASSERT(m_graph[valueInPredecessor].op == SetLocal || m_graph[valueInPredecessor].op == Phi || m_graph[valueInPredecessor].op == Flush || (m_graph[valueInPredecessor].op == SetArgument && stackType == ArgumentPhiStack)); + ASSERT(m_graph[valueInPredecessor].op == SetLocal + || m_graph[valueInPredecessor].op == Phi + || m_graph[valueInPredecessor].op == Flush + || (m_graph[valueInPredecessor].op == SetArgument + && stackType == ArgumentPhiStack)); VariableAccessData* dataForPredecessor = m_graph[valueInPredecessor].variableAccessData(); @@ -2309,7 +2415,7 @@ void ByteCodeParser::processPhiStack() continue; } - NodeIndex newPhi = addToGraph(Phi, OpInfo(dataForPhi)); + NodeIndex newPhi = insertPhiNode(OpInfo(dataForPhi), entry.m_block); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" Splitting @%u, created @%u.\n", entry.m_phi, newPhi); @@ -2349,10 +2455,9 @@ void ByteCodeParser::fixVariableAccessPredictions() void ByteCodeParser::linkBlock(BasicBlock* block, Vector<BlockIndex>& possibleTargets) { - ASSERT(block->end != NoNode); ASSERT(!block->isLinked); - ASSERT(block->end > block->begin); - Node& node = m_graph[block->end - 1]; + ASSERT(!block->isEmpty()); + Node& node = m_graph[block->last()]; ASSERT(node.isTerminal()); switch (node.op) { @@ -2416,7 +2521,7 @@ void ByteCodeParser::determineReachability() BasicBlock* block = m_graph.m_blocks[index].get(); ASSERT(block->isLinked); - Node& node = m_graph[block->end - 1]; + Node& node = m_graph[block->last()]; ASSERT(node.isTerminal()); if (node.isJump()) @@ -2435,8 +2540,13 @@ void ByteCodeParser::buildOperandMapsIfNecessary() for (size_t i = 0; i < m_codeBlock->numberOfIdentifiers(); ++i) m_identifierMap.add(m_codeBlock->identifier(i).impl(), i); - for (size_t i = 0; i < m_codeBlock->numberOfConstantRegisters(); ++i) - m_jsValueMap.add(JSValue::encode(m_codeBlock->getConstant(i + FirstConstantRegisterIndex)), i + FirstConstantRegisterIndex); + for (size_t i = 0; i < m_codeBlock->numberOfConstantRegisters(); ++i) { + JSValue value = m_codeBlock->getConstant(i + FirstConstantRegisterIndex); + if (!value) + m_emptyJSValueIndex = i + FirstConstantRegisterIndex; + else + m_jsValueMap.add(JSValue::encode(value), i + FirstConstantRegisterIndex); + } m_haveBuiltOperandMaps = true; } @@ -2486,6 +2596,15 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(ByteCodeParser* byteCodeParse } for (size_t i = 0; i < codeBlock->numberOfConstantRegisters(); ++i) { JSValue value = codeBlock->getConstant(i + FirstConstantRegisterIndex); + if (!value) { + if (byteCodeParser->m_emptyJSValueIndex == UINT_MAX) { + byteCodeParser->m_emptyJSValueIndex = byteCodeParser->m_codeBlock->numberOfConstantRegisters() + FirstConstantRegisterIndex; + byteCodeParser->m_codeBlock->addConstant(JSValue()); + byteCodeParser->m_constants.append(ConstantRecord()); + } + m_constantRemap[i] = byteCodeParser->m_emptyJSValueIndex; + continue; + } pair<JSValueMap::iterator, bool> result = byteCodeParser->m_jsValueMap.add(JSValue::encode(value), byteCodeParser->m_codeBlock->numberOfConstantRegisters() + FirstConstantRegisterIndex); if (result.second) { byteCodeParser->m_codeBlock->addConstant(value); @@ -2527,6 +2646,16 @@ void ByteCodeParser::parseCodeBlock() { CodeBlock* codeBlock = m_inlineStackTop->m_codeBlock; +#if DFG_ENABLE(DEBUG_VERBOSE) + dataLog("Parsing code block %p. codeType = %s, numCapturedVars = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n", + codeBlock, + codeTypeToString(codeBlock->codeType()), + codeBlock->m_numCapturedVars, + codeBlock->needsFullScopeChain()?"true":"false", + codeBlock->ownerExecutable()->needsActivation()?"true":"false", + codeBlock->ownerExecutable()->isStrictMode()?"true":"false"); +#endif + for (unsigned jumpTargetIndex = 0; jumpTargetIndex <= codeBlock->numberOfJumpTargets(); ++jumpTargetIndex) { // The maximum bytecode offset to go into the current basicblock is either the next jump target, or the end of the instructions. unsigned limit = jumpTargetIndex < codeBlock->numberOfJumpTargets() ? codeBlock->jumpTarget(jumpTargetIndex) : codeBlock->instructions().size(); @@ -2539,7 +2668,7 @@ void ByteCodeParser::parseCodeBlock() do { if (!m_currentBlock) { // Check if we can use the last block. - if (!m_graph.m_blocks.isEmpty() && m_graph.m_blocks.last()->begin == m_graph.m_blocks.last()->end) { + if (!m_graph.m_blocks.isEmpty() && m_graph.m_blocks.last()->isEmpty()) { // This must be a block belonging to us. ASSERT(m_inlineStackTop->m_unlinkedBlocks.last().m_blockIndex == m_graph.m_blocks.size() - 1); // Either the block is linkable or it isn't. If it's linkable then it's the last @@ -2557,7 +2686,7 @@ void ByteCodeParser::parseCodeBlock() #endif m_currentBlock->bytecodeBegin = m_currentIndex; } else { - OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(m_currentIndex, m_graph.size(), m_numArguments, m_numLocals)); + OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(m_currentIndex, m_numArguments, m_numLocals)); #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("Creating basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); #endif @@ -2580,10 +2709,8 @@ void ByteCodeParser::parseCodeBlock() // are at the end of an inline function, or we realized that we // should stop parsing because there was a return in the first // basic block. - ASSERT(m_currentBlock->begin == m_graph.size() || m_graph.last().isTerminal() || (m_currentIndex == codeBlock->instructions().size() && m_inlineStackTop->m_inlineCallFrame) || !shouldContinueParsing); + ASSERT(m_currentBlock->isEmpty() || m_graph.last().isTerminal() || (m_currentIndex == codeBlock->instructions().size() && m_inlineStackTop->m_inlineCallFrame) || !shouldContinueParsing); - m_currentBlock->end = m_graph.size(); - if (!shouldContinueParsing) return; @@ -2600,6 +2727,11 @@ bool ByteCodeParser::parse() // Set during construction. ASSERT(!m_currentIndex); +#if DFG_ENABLE(ALL_VARIABLES_CAPTURED) + // We should be pretending that the code has an activation. + ASSERT(m_graph.needsActivation()); +#endif + InlineStackEntry inlineStackEntry(this, m_codeBlock, m_profiledBlock, NoBlock, InvalidVirtualRegister, 0, InvalidVirtualRegister, InvalidVirtualRegister, CodeForCall); parseCodeBlock(); diff --git a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h index 3481f99e8..16793bb46 100644 --- a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h +++ b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h @@ -420,6 +420,13 @@ public: move(arg2, GPRInfo::argumentGPR2); move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); } + + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, ImmPtr arg2) + { + move(arg1, GPRInfo::argumentGPR1); + move(arg2, GPRInfo::argumentGPR2); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImmPtr arg1, GPRReg arg2) { @@ -427,6 +434,13 @@ public: move(arg1, GPRInfo::argumentGPR1); move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); } + + ALWAYS_INLINE void setupArgumentsWithExecState(ImmPtr arg1, GPRReg arg2) + { + move(arg2, GPRInfo::argumentGPR2); // Move this first, so setting arg1 does not trample! + move(arg1, GPRInfo::argumentGPR1); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, TrustedImm32 arg2) { diff --git a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp index ac1e26c19..b4e75f808 100644 --- a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp @@ -82,15 +82,16 @@ private: dumpOperands(block->valuesAtHead, WTF::dataFile()); dataLog("\n"); #endif - for (NodeIndex nodeIndex = block->begin; nodeIndex < block->end; ++nodeIndex) { + for (unsigned i = 0; i < block->size(); ++i) { + NodeIndex nodeIndex = block->at(i); if (!m_graph[nodeIndex].shouldGenerate()) continue; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: ", Graph::opName(m_graph[nodeIndex].op), nodeIndex); + dataLog(" %s @%u: ", Graph::opName(static_cast<NodeType>(m_graph[nodeIndex].op)), nodeIndex); m_state.dump(WTF::dataFile()); dataLog("\n"); #endif - if (!m_state.execute(nodeIndex)) + if (!m_state.execute(i)) break; } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) @@ -120,6 +121,9 @@ private: AbstractState m_state; bool m_changed; +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + unsigned m_count; +#endif }; void performCFA(Graph& graph) diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index a3c27ebc1..82e1b4609 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -43,9 +43,6 @@ public: for (unsigned i = 0; i < m_graph.size(); ++i) m_replacements[i] = NoNode; - - for (unsigned i = 0; i < LastNodeId; ++i) - m_lastSeen[i] = NoNode; } void run() @@ -71,68 +68,14 @@ private: return canonicalize(nodeUse.indexUnchecked()); } - // Computes where the search for a candidate for CSE should start. Don't call - // this directly; call startIndex() instead as it does logging in debug mode. - NodeIndex computeStartIndexForChildren(NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode) + unsigned endIndexForPureCSE() { - const unsigned limit = 300; - - NodeIndex start = m_start; - if (m_compileIndex - start > limit) - start = m_compileIndex - limit; - - ASSERT(start >= m_start); - - NodeIndex child = canonicalize(child1); - if (child == NoNode) - return start; - - if (start < child) - start = child; - - child = canonicalize(child2); - if (child == NoNode) - return start; - - if (start < child) - start = child; - - child = canonicalize(child3); - if (child == NoNode) - return start; - - if (start < child) - start = child; - - return start; - } - - NodeIndex startIndexForChildren(NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode) - { - NodeIndex result = computeStartIndexForChildren(child1, child2, child3); -#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" lookback %u: ", result); -#endif - return result; - } - - NodeIndex startIndex() - { - Node& node = m_graph[m_compileIndex]; - return startIndexForChildren( - node.child1().indexUnchecked(), - node.child2().indexUnchecked(), - node.child3().indexUnchecked()); - } - - NodeIndex endIndexForPureCSE() - { - NodeIndex result = m_lastSeen[m_graph[m_compileIndex].op & NodeIdMask]; - if (result == NoNode) + unsigned result = m_lastSeen[m_graph[m_compileIndex].op]; + if (result == UINT_MAX) result = 0; else result++; - ASSERT(result <= m_compileIndex); + ASSERT(result <= m_indexInBlock); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" limit %u: ", result); #endif @@ -145,13 +88,16 @@ private: NodeIndex child2 = canonicalize(node.child2()); NodeIndex child3 = canonicalize(node.child3()); - NodeIndex start = startIndex(); - for (NodeIndex index = endIndexForPureCSE(); index-- > start;) { + for (unsigned i = endIndexForPureCSE(); i--;) { + NodeIndex index = m_currentBlock->at(i); + if (index == child1 || index == child2 || index == child3) + break; + Node& otherNode = m_graph[index]; if (node.op != otherNode.op) continue; - if (node.arithNodeFlagsForCompare() != otherNode.arithNodeFlagsForCompare()) + if (node.arithNodeFlags() != otherNode.arithNodeFlags()) continue; NodeIndex otherChild = canonicalize(otherNode.child1()); @@ -201,9 +147,9 @@ private: bool clobbersWorld(NodeIndex nodeIndex) { Node& node = m_graph[nodeIndex]; - if (node.op & NodeClobbersWorld) + if (node.flags & NodeClobbersWorld) return true; - if (!(node.op & NodeMightClobber)) + if (!(node.flags & NodeMightClobber)) return false; switch (node.op) { case ValueAdd: @@ -229,11 +175,14 @@ private: NodeIndex child2 = canonicalize(node.child2()); NodeIndex child3 = canonicalize(node.child3()); - NodeIndex start = startIndex(); - for (NodeIndex index = m_compileIndex; index-- > start;) { + for (unsigned i = m_indexInBlock; i--;) { + NodeIndex index = m_currentBlock->at(i); + if (index == child1 || index == child2 || index == child3) + break; + Node& otherNode = m_graph[index]; if (node.op == otherNode.op - && node.arithNodeFlagsForCompare() == otherNode.arithNodeFlagsForCompare()) { + && node.arithNodeFlags() == otherNode.arithNodeFlags()) { NodeIndex otherChild = canonicalize(otherNode.child1()); if (otherChild == NoNode) return index; @@ -258,8 +207,8 @@ private: NodeIndex globalVarLoadElimination(unsigned varNumber, JSGlobalObject* globalObject) { - NodeIndex start = startIndexForChildren(); - for (NodeIndex index = m_compileIndex; index-- > start;) { + for (unsigned i = m_indexInBlock; i--;) { + NodeIndex index = m_currentBlock->at(i); Node& node = m_graph[index]; switch (node.op) { case GetGlobalVar: @@ -281,8 +230,11 @@ private: NodeIndex getByValLoadElimination(NodeIndex child1, NodeIndex child2) { - NodeIndex start = startIndexForChildren(child1, child2); - for (NodeIndex index = m_compileIndex; index-- > start;) { + for (unsigned i = m_indexInBlock; i--;) { + NodeIndex index = m_currentBlock->at(i); + if (index == child1 || index == canonicalize(child2)) + break; + Node& node = m_graph[index]; switch (node.op) { case GetByVal: @@ -322,8 +274,11 @@ private: bool checkFunctionElimination(JSFunction* function, NodeIndex child1) { - NodeIndex start = startIndexForChildren(child1); - for (NodeIndex index = endIndexForPureCSE(); index-- > start;) { + for (unsigned i = endIndexForPureCSE(); i--;) { + NodeIndex index = m_currentBlock->at(i); + if (index == child1) + break; + Node& node = m_graph[index]; if (node.op == CheckFunction && node.child1() == child1 && node.function() == function) return true; @@ -333,8 +288,11 @@ private: bool checkStructureLoadElimination(const StructureSet& structureSet, NodeIndex child1) { - NodeIndex start = startIndexForChildren(child1); - for (NodeIndex index = m_compileIndex; index-- > start;) { + for (unsigned i = m_indexInBlock; i--;) { + NodeIndex index = m_currentBlock->at(i); + if (index == child1) + break; + Node& node = m_graph[index]; switch (node.op) { case CheckStructure: @@ -376,8 +334,11 @@ private: NodeIndex getByOffsetLoadElimination(unsigned identifierNumber, NodeIndex child1) { - NodeIndex start = startIndexForChildren(child1); - for (NodeIndex index = m_compileIndex; index-- > start;) { + for (unsigned i = m_indexInBlock; i--;) { + NodeIndex index = m_currentBlock->at(i); + if (index == child1) + break; + Node& node = m_graph[index]; switch (node.op) { case GetByOffset: @@ -419,8 +380,11 @@ private: NodeIndex getPropertyStorageLoadElimination(NodeIndex child1) { - NodeIndex start = startIndexForChildren(child1); - for (NodeIndex index = m_compileIndex; index-- > start;) { + for (unsigned i = m_indexInBlock; i--;) { + NodeIndex index = m_currentBlock->at(i); + if (index == child1) + break; + Node& node = m_graph[index]; switch (node.op) { case GetPropertyStorage: @@ -455,8 +419,11 @@ private: NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, bool hasIntegerIndexPrediction) { - NodeIndex start = startIndexForChildren(child1); - for (NodeIndex index = m_compileIndex; index-- > start;) { + for (unsigned i = m_indexInBlock; i--;) { + NodeIndex index = m_currentBlock->at(i); + if (index == child1) + break; + Node& node = m_graph[index]; switch (node.op) { case GetIndexedPropertyStorage: { @@ -493,8 +460,8 @@ private: NodeIndex getScopeChainLoadElimination(unsigned depth) { - NodeIndex start = startIndexForChildren(); - for (NodeIndex index = endIndexForPureCSE(); index-- > start;) { + for (unsigned i = endIndexForPureCSE(); i--;) { + NodeIndex index = m_currentBlock->at(i); Node& node = m_graph[index]; if (node.op == GetScopeChain && node.scopeChainDepth() == depth) @@ -539,7 +506,7 @@ private: #endif Node& node = m_graph[m_compileIndex]; - node.op = Phantom; + node.setOpAndDefaultFlags(Phantom); node.setRefCount(1); // At this point we will eliminate all references to this node. @@ -555,14 +522,14 @@ private: Node& node = m_graph[m_compileIndex]; ASSERT(node.refCount() == 1); ASSERT(node.mustGenerate()); - node.op = Phantom; + node.setOpAndDefaultFlags(Phantom); } void performNodeCSE(Node& node) { bool shouldGenerate = node.shouldGenerate(); - if (node.op & NodeHasVarArgs) { + if (node.flags & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) performSubstitution(m_graph.m_varArgChildren[childIdx], shouldGenerate); } else { @@ -575,7 +542,7 @@ private: return; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: ", Graph::opName(m_graph[m_compileIndex].op), m_compileIndex); + dataLog(" %s @%u: ", Graph::opName(static_cast<NodeType>(m_graph[m_compileIndex].op)), m_compileIndex); #endif // NOTE: there are some nodes that we deliberately don't CSE even though we @@ -598,6 +565,7 @@ private: case BitURShift: case ArithAdd: case ArithSub: + case ArithNegate: case ArithMul: case ArithMod: case ArithDiv: @@ -701,7 +669,7 @@ private: break; } - m_lastSeen[node.op & NodeIdMask] = m_compileIndex; + m_lastSeen[node.op] = m_indexInBlock; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog("\n"); #endif @@ -709,16 +677,21 @@ private: void performBlockCSE(BasicBlock& block) { - m_start = block.begin; - NodeIndex end = block.end; - for (m_compileIndex = m_start; m_compileIndex < end; ++m_compileIndex) + m_currentBlock = █ + for (unsigned i = 0; i < LastNodeType; ++i) + m_lastSeen[i] = UINT_MAX; + + for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) { + m_compileIndex = block[m_indexInBlock]; performNodeCSE(m_graph[m_compileIndex]); + } } - NodeIndex m_start; + BasicBlock* m_currentBlock; NodeIndex m_compileIndex; + unsigned m_indexInBlock; Vector<NodeIndex, 16> m_replacements; - FixedArray<NodeIndex, LastNodeId> m_lastSeen; + FixedArray<unsigned, LastNodeType> m_lastSeen; }; void performCSE(Graph& graph) diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.h b/Source/JavaScriptCore/dfg/DFGCapabilities.h index e339714e9..6509dbc3d 100644 --- a/Source/JavaScriptCore/dfg/DFGCapabilities.h +++ b/Source/JavaScriptCore/dfg/DFGCapabilities.h @@ -57,11 +57,13 @@ inline bool mightCompileFunctionForConstruct(CodeBlock* codeBlock) inline bool mightInlineFunctionForCall(CodeBlock* codeBlock) { - return codeBlock->instructionCount() <= Options::maximumFunctionForCallInlineCandidateInstructionCount; + return codeBlock->instructionCount() <= Options::maximumFunctionForCallInlineCandidateInstructionCount + && !codeBlock->ownerExecutable()->needsActivation(); } inline bool mightInlineFunctionForConstruct(CodeBlock* codeBlock) { - return codeBlock->instructionCount() <= Options::maximumFunctionForConstructInlineCandidateInstructionCount; + return codeBlock->instructionCount() <= Options::maximumFunctionForConstructInlineCandidateInstructionCount + && !codeBlock->ownerExecutable()->needsActivation(); } // Opcode checking. @@ -84,6 +86,7 @@ inline bool canCompileOpcode(OpcodeID opcodeID) case op_post_dec: case op_add: case op_sub: + case op_negate: case op_mul: case op_mod: case op_div: @@ -151,21 +154,14 @@ inline bool canCompileOpcode(OpcodeID opcodeID) case op_throw_reference_error: case op_call: case op_construct: - return true; - - // Opcodes we support conditionally. Enabling these opcodes currently results in - // performance regressions. Each node that we disable under restrictions has a - // comment describing what we know about the regression so far. - - // Regresses string-validate-input, probably because it uses comparisons (< and >) - // on strings, which currently will cause speculation failures in some cases. case op_new_regexp: -#if DFG_ENABLE(RESTRICTIONS) - return false; -#else + case op_init_lazy_reg: + case op_create_activation: + case op_tear_off_activation: + case op_new_func: + case op_new_func_exp: return true; -#endif - + default: return false; } @@ -191,6 +187,14 @@ inline bool canInlineOpcode(OpcodeID opcodeID) case op_new_regexp: return false; + // We don't support inlining code that creates activations or has nested functions. + case op_init_lazy_reg: + case op_create_activation: + case op_tear_off_activation: + case op_new_func: + case op_new_func_exp: + return false; + default: return canCompileOpcode(opcodeID); } diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h index 330504c3e..8ff1e5cdd 100644 --- a/Source/JavaScriptCore/dfg/DFGCommon.h +++ b/Source/JavaScriptCore/dfg/DFGCommon.h @@ -53,7 +53,7 @@ #define DFG_ENABLE_CONSISTENCY_CHECK 0 // Emit a breakpoint into the head of every generated function, to aid debugging in GDB. #define DFG_ENABLE_JIT_BREAK_ON_EVERY_FUNCTION 0 -// Emit a breakpoint into the head of every generated node, to aid debugging in GDB. +// Emit a breakpoint into the head of every generated block, to aid debugging in GDB. #define DFG_ENABLE_JIT_BREAK_ON_EVERY_BLOCK 0 // Emit a breakpoint into the head of every generated node, to aid debugging in GDB. #define DFG_ENABLE_JIT_BREAK_ON_EVERY_NODE 0 @@ -69,10 +69,11 @@ #define DFG_ENABLE_OSR_ENTRY ENABLE(DFG_JIT) // Generate stats on how successful we were in making use of the DFG jit, and remaining on the hot path. #define DFG_ENABLE_SUCCESS_STATS 0 -// Used to enable conditionally supported opcodes that currently result in performance regressions. -#define DFG_ENABLE_RESTRICTIONS 1 // Enable verification that the DFG is able to insert code for control flow edges. #define DFG_ENABLE_EDGE_CODE_VERIFICATION 0 +// Pretend that all variables in the top-level code block got captured. Great +// for testing code gen for activations. +#define DFG_ENABLE_ALL_VARIABLES_CAPTURED 0 namespace JSC { namespace DFG { diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp index 124d7e637..a0af3e6ad 100644 --- a/Source/JavaScriptCore/dfg/DFGDriver.cpp +++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp @@ -34,6 +34,7 @@ #include "DFGCSEPhase.h" #include "DFGJITCompiler.h" #include "DFGPredictionPropagationPhase.h" +#include "DFGRedundantPhiEliminationPhase.h" #include "DFGVirtualRegisterAllocationPhase.h" namespace JSC { namespace DFG { @@ -58,6 +59,7 @@ inline bool compile(CompileMode compileMode, JSGlobalData& globalData, CodeBlock if (compileMode == CompileFunction) dfg.predictArgumentTypes(); + performRedundantPhiElimination(dfg); performArithNodeFlagsInference(dfg); performPredictionPropagation(dfg); performCSE(dfg); @@ -70,18 +72,19 @@ inline bool compile(CompileMode compileMode, JSGlobalData& globalData, CodeBlock #endif JITCompiler dataFlowJIT(dfg); + bool result; if (compileMode == CompileFunction) { ASSERT(jitCodeWithArityCheck); - dataFlowJIT.compileFunction(jitCode, *jitCodeWithArityCheck); + result = dataFlowJIT.compileFunction(jitCode, *jitCodeWithArityCheck); } else { ASSERT(compileMode == CompileOther); ASSERT(!jitCodeWithArityCheck); - dataFlowJIT.compile(jitCode); + result = dataFlowJIT.compile(jitCode); } - return true; + return result; } bool tryCompile(JSGlobalData& globalData, CodeBlock* codeBlock, JITCode& jitCode) diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp index b8eec93c7..900251e10 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.cpp +++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp @@ -41,7 +41,7 @@ static const char* dfgOpNames[] = { const char *Graph::opName(NodeType op) { - return dfgOpNames[op & NodeIdMask]; + return dfgOpNames[op]; } const char* Graph::nameOfVariableAccessData(VariableAccessData* variableAccessData) @@ -120,7 +120,7 @@ void Graph::dumpCodeOrigin(NodeIndex nodeIndex) void Graph::dump(NodeIndex nodeIndex) { Node& node = at(nodeIndex); - NodeType op = node.op; + NodeType op = static_cast<NodeType>(node.op); unsigned refCount = node.refCount(); bool skipped = !refCount; @@ -157,7 +157,7 @@ void Graph::dump(NodeIndex nodeIndex) dataLog("-"); dataLog(">\t%s(", opName(op)); bool hasPrinted = false; - if (op & NodeHasVarArgs) { + if (node.flags & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) { if (hasPrinted) dataLog(", "); @@ -175,8 +175,8 @@ void Graph::dump(NodeIndex nodeIndex) hasPrinted = !!node.child1(); } - if (node.hasArithNodeFlags()) { - dataLog("%s%s", hasPrinted ? ", " : "", arithNodeFlagsAsString(node.rawArithNodeFlags())); + if (node.arithNodeFlags()) { + dataLog("%s%s", hasPrinted ? ", " : "", arithNodeFlagsAsString(node.arithNodeFlags())); hasPrinted = true; } if (node.hasVarNumber()) { @@ -265,6 +265,12 @@ void Graph::dump() for (size_t b = 0; b < m_blocks.size(); ++b) { BasicBlock* block = m_blocks[b].get(); dataLog("Block #%u (bc#%u): %s%s\n", (int)b, block->bytecodeBegin, block->isReachable ? "" : " (skipped)", block->isOSRTarget ? " (OSR target)" : ""); + dataLog(" Phi Nodes:\n"); + for (size_t i = 0; i < block->phis.size(); ++i) { + // Dumping the dead Phi nodes is just annoying! + if (at(block->phis[i]).refCount()) + dump(block->phis[i]); + } dataLog(" vars before: "); if (block->cfaHasVisited) dumpOperands(block->valuesAtHead, WTF::dataFile()); @@ -274,8 +280,8 @@ void Graph::dump() dataLog(" var links: "); dumpOperands(block->variablesAtHead, WTF::dataFile()); dataLog("\n"); - for (size_t i = block->begin; i < block->end; ++i) - dump(i); + for (size_t i = 0; i < block->size(); ++i) + dump(block->at(i)); dataLog(" vars after: "); if (block->cfaHasVisited) dumpOperands(block->valuesAtTail, WTF::dataFile()); @@ -283,15 +289,12 @@ void Graph::dump() dataLog("<empty>"); dataLog("\n"); } - dataLog("Phi Nodes:\n"); - for (size_t i = m_blocks.last()->end; i < size(); ++i) - dump(i); } // FIXME: Convert this to be iterative, not recursive. #define DO_TO_CHILDREN(node, thingToDo) do { \ Node& _node = (node); \ - if (_node.op & NodeHasVarArgs) { \ + if (_node.flags & NodeHasVarArgs) { \ for (unsigned _childIdx = _node.firstChild(); \ _childIdx < _node.firstChild() + _node.numChildren(); \ _childIdx++) \ diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h index 88d6a4eec..bacbac827 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.h +++ b/Source/JavaScriptCore/dfg/DFGGraph.h @@ -177,6 +177,12 @@ public: return Node::shouldSpeculateInteger(left, right) && add.canSpeculateInteger(); } + bool negateShouldSpeculateInteger(Node& negate) + { + ASSERT(negate.op == ArithNegate); + return at(negate.child1()).shouldSpeculateInteger() && negate.canSpeculateInteger(); + } + bool addShouldSpeculateInteger(NodeIndex nodeIndex) { return addShouldSpeculateInteger(at(nodeIndex)); @@ -305,6 +311,41 @@ public: return MethodOfGettingAValueProfile(valueProfileFor(nodeIndex)); } + bool needsActivation() const + { +#if DFG_ENABLE(ALL_VARIABLES_CAPTURED) + return true; +#else + return m_codeBlock->needsFullScopeChain() && m_codeBlock->codeType() != GlobalCode; +#endif + } + + // Pass an argument index. Currently it's ignored, but that's somewhat + // of a bug. + bool argumentIsCaptured(int) const + { + return needsActivation(); + } + bool localIsCaptured(int operand) const + { +#if DFG_ENABLE(ALL_VARIABLES_CAPTURED) + return operand < m_codeBlock->m_numVars; +#else + return operand < m_codeBlock->m_numCapturedVars; +#endif + } + + bool isCaptured(int operand) const + { + if (operandIsArgument(operand)) + return argumentIsCaptured(operandToArgument(operand)); + return localIsCaptured(operand); + } + bool isCaptured(VirtualRegister virtualRegister) const + { + return isCaptured(static_cast<int>(virtualRegister)); + } + JSGlobalData& m_globalData; CodeBlock* m_codeBlock; CodeBlock* m_profiledBlock; diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp index 7b2bbc788..af98f8d7a 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp @@ -44,7 +44,7 @@ void JITCompiler::linkOSRExits() for (unsigned i = 0; i < codeBlock()->numberOfOSRExits(); ++i) { OSRExit& exit = codeBlock()->osrExit(i); exit.m_check.initialJump().link(this); - store32(Imm32(i), &globalData()->osrExitIndex); + store32(TrustedImm32(i), &globalData()->osrExitIndex); beginUninterruptedSequence(); exit.m_check.switchToLateJump(jump()); endUninterruptedSequence(); @@ -75,7 +75,7 @@ void JITCompiler::compileBody(SpeculativeJIT& speculative) breakpoint(); #endif - addPtr(Imm32(1), AbsoluteAddress(codeBlock()->addressOfSpeculativeSuccessCounter())); + addPtr(TrustedImm32(1), AbsoluteAddress(codeBlock()->addressOfSpeculativeSuccessCounter())); bool compiledSpeculative = speculative.compile(); ASSERT_UNUSED(compiledSpeculative, compiledSpeculative); @@ -195,7 +195,7 @@ void JITCompiler::link(LinkBuffer& linkBuffer) codeBlock()->shrinkWeakReferenceTransitionsToFit(); } -void JITCompiler::compile(JITCode& entry) +bool JITCompiler::compile(JITCode& entry) { compileEntry(); SpeculativeJIT speculative(*this); @@ -204,14 +204,17 @@ void JITCompiler::compile(JITCode& entry) // Create OSR entry trampolines if necessary. speculative.createOSREntries(); - LinkBuffer linkBuffer(*m_globalData, this, m_codeBlock); + LinkBuffer linkBuffer(*m_globalData, this, m_codeBlock, JITCompilationCanFail); + if (linkBuffer.didFailToAllocate()) + return false; link(linkBuffer); speculative.linkOSREntries(linkBuffer); entry = JITCode(linkBuffer.finalizeCode(), JITCode::DFGJIT); + return true; } -void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck) +bool JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck) { compileEntry(); @@ -222,7 +225,7 @@ void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWi Label fromArityCheck(this); // Plant a check that sufficient space is available in the RegisterFile. // FIXME: https://bugs.webkit.org/show_bug.cgi?id=56291 - addPtr(Imm32(m_codeBlock->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1); + addPtr(TrustedImm32(m_codeBlock->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1); Jump registerFileCheck = branchPtr(Below, AbsoluteAddress(m_globalData->interpreter->registerFile().addressOfEnd()), GPRInfo::regT1); // Return here after register file check. Label fromRegisterFileCheck = label(); @@ -258,7 +261,7 @@ void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWi compileEntry(); load32(AssemblyHelpers::payloadFor((VirtualRegister)RegisterFile::ArgumentCount), GPRInfo::regT1); - branch32(AboveOrEqual, GPRInfo::regT1, Imm32(m_codeBlock->numParameters())).linkTo(fromArityCheck, this); + branch32(AboveOrEqual, GPRInfo::regT1, TrustedImm32(m_codeBlock->numParameters())).linkTo(fromArityCheck, this); move(stackPointerRegister, GPRInfo::argumentGPR0); poke(GPRInfo::callFrameRegister, OBJECT_OFFSETOF(struct JITStackFrame, callFrame) / sizeof(void*)); token = beginCall(); @@ -272,7 +275,9 @@ void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWi // === Link === - LinkBuffer linkBuffer(*m_globalData, this, m_codeBlock); + LinkBuffer linkBuffer(*m_globalData, this, m_codeBlock, JITCompilationCanFail); + if (linkBuffer.didFailToAllocate()) + return false; link(linkBuffer); speculative.linkOSREntries(linkBuffer); @@ -282,6 +287,7 @@ void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWi entryWithArityCheck = linkBuffer.locationOf(arityCheck); entry = JITCode(linkBuffer.finalizeCode(), JITCode::DFGJIT); + return true; } } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.h b/Source/JavaScriptCore/dfg/DFGJITCompiler.h index a0c68fe4b..2df2703b0 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.h +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.h @@ -194,8 +194,8 @@ public: { } - void compile(JITCode& entry); - void compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck); + bool compile(JITCode& entry); + bool compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck); // Accessors for properties. Graph& graph() { return m_graph; } @@ -297,12 +297,13 @@ public: // value of (None, []). But the old JIT may stash some values there. So we really // need (Top, TOP). for (size_t argument = 0; argument < basicBlock.variablesAtHead.numberOfArguments(); ++argument) { - if (basicBlock.variablesAtHead.argument(argument) == NoNode) + NodeIndex nodeIndex = basicBlock.variablesAtHead.argument(argument); + if (nodeIndex == NoNode || !m_graph[nodeIndex].shouldGenerate()) entry->m_expectedValues.argument(argument).makeTop(); } for (size_t local = 0; local < basicBlock.variablesAtHead.numberOfLocals(); ++local) { NodeIndex nodeIndex = basicBlock.variablesAtHead.local(local); - if (nodeIndex == NoNode) + if (nodeIndex == NoNode || !m_graph[nodeIndex].shouldGenerate()) entry->m_expectedValues.local(local).makeTop(); else if (m_graph[nodeIndex].variableAccessData()->shouldUseDoubleFormat()) entry->m_localsForcedDouble.set(local); diff --git a/Source/JavaScriptCore/dfg/DFGNode.cpp b/Source/JavaScriptCore/dfg/DFGNode.cpp new file mode 100644 index 000000000..c53817ba9 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGNode.cpp @@ -0,0 +1,81 @@ +/* + * 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 "DFGNode.h" + +#if ENABLE(DFG_JIT) + +namespace JSC { namespace DFG { + +const char* arithNodeFlagsAsString(NodeFlags flags) +{ + flags &= NodeArithMask; + + if (!flags) + return "<empty>"; + + static const int size = 64; + static char description[size]; + BoundsCheckedPointer<char> ptr(description, size); + + bool hasPrinted = false; + + if (flags & NodeUsedAsNumber) { + ptr.strcat("UsedAsNum"); + hasPrinted = true; + } + + if (flags & NodeNeedsNegZero) { + if (hasPrinted) + ptr.strcat("|"); + ptr.strcat("NeedsNegZero"); + hasPrinted = true; + } + + if (flags & NodeMayOverflow) { + if (hasPrinted) + ptr.strcat("|"); + ptr.strcat("MayOverflow"); + hasPrinted = true; + } + + if (flags & NodeMayNegZero) { + if (hasPrinted) + ptr.strcat("|"); + ptr.strcat("MayNegZero"); + hasPrinted = true; + } + + *ptr++ = 0; + + return description; +} + + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index 87dae7786..b672b67c5 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011, 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 @@ -57,36 +57,50 @@ struct StructureTransitionData { } }; -typedef unsigned ArithNodeFlags; -#define NodeUseBottom 0x00 -#define NodeUsedAsNumber 0x01 -#define NodeNeedsNegZero 0x02 -#define NodeUsedAsMask 0x03 -#define NodeMayOverflow 0x04 -#define NodeMayNegZero 0x08 -#define NodeBehaviorMask 0x0c +// Entries in the NodeType enum (below) are composed of an id, a result type (possibly none) +// and some additional informative flags (must generate, is constant, etc). +#define NodeResultMask 0xF +#define NodeResultJS 0x1 +#define NodeResultNumber 0x2 +#define NodeResultInt32 0x3 +#define NodeResultBoolean 0x4 +#define NodeResultStorage 0x5 +#define NodeMustGenerate 0x10 // set on nodes that have side effects, and may not trivially be removed by DCE. +#define NodeHasVarArgs 0x20 +#define NodeClobbersWorld 0x40 +#define NodeMightClobber 0x80 +#define NodeArithMask 0xF00 +#define NodeUseBottom 0x000 +#define NodeUsedAsNumber 0x100 +#define NodeNeedsNegZero 0x200 +#define NodeUsedAsMask 0x300 +#define NodeMayOverflow 0x400 +#define NodeMayNegZero 0x800 +#define NodeBehaviorMask 0xc00 -static inline bool nodeUsedAsNumber(ArithNodeFlags flags) +typedef uint16_t NodeFlags; + +static inline bool nodeUsedAsNumber(NodeFlags flags) { return !!(flags & NodeUsedAsNumber); } -static inline bool nodeCanTruncateInteger(ArithNodeFlags flags) +static inline bool nodeCanTruncateInteger(NodeFlags flags) { return !nodeUsedAsNumber(flags); } -static inline bool nodeCanIgnoreNegativeZero(ArithNodeFlags flags) +static inline bool nodeCanIgnoreNegativeZero(NodeFlags flags) { return !(flags & NodeNeedsNegZero); } -static inline bool nodeMayOverflow(ArithNodeFlags flags) +static inline bool nodeMayOverflow(NodeFlags flags) { return !!(flags & NodeMayOverflow); } -static inline bool nodeCanSpeculateInteger(ArithNodeFlags flags) +static inline bool nodeCanSpeculateInteger(NodeFlags flags) { if (flags & NodeMayOverflow) return !nodeUsedAsNumber(flags); @@ -97,67 +111,7 @@ static inline bool nodeCanSpeculateInteger(ArithNodeFlags flags) return true; } -static inline const char* arithNodeFlagsAsString(ArithNodeFlags flags) -{ - if (!flags) - return "<empty>"; - - static const int size = 64; - static char description[size]; - BoundsCheckedPointer<char> ptr(description, size); - - bool hasPrinted = false; - - if (flags & NodeUsedAsNumber) { - ptr.strcat("UsedAsNum"); - hasPrinted = true; - } - - if (flags & NodeNeedsNegZero) { - if (hasPrinted) - ptr.strcat("|"); - ptr.strcat("NeedsNegZero"); - hasPrinted = true; - } - - if (flags & NodeMayOverflow) { - if (hasPrinted) - ptr.strcat("|"); - ptr.strcat("MayOverflow"); - hasPrinted = true; - } - - if (flags & NodeMayNegZero) { - if (hasPrinted) - ptr.strcat("|"); - ptr.strcat("MayNegZero"); - hasPrinted = true; - } - - *ptr++ = 0; - - return description; -} - -// Entries in the NodeType enum (below) are composed of an id, a result type (possibly none) -// and some additional informative flags (must generate, is constant, etc). -#define NodeIdMask 0xFFF -#define NodeResultMask 0xF000 -#define NodeMustGenerate 0x10000 // set on nodes that have side effects, and may not trivially be removed by DCE. -#define NodeIsConstant 0x20000 -#define NodeIsJump 0x40000 -#define NodeIsBranch 0x80000 -#define NodeIsTerminal 0x100000 -#define NodeHasVarArgs 0x200000 -#define NodeClobbersWorld 0x400000 -#define NodeMightClobber 0x800000 - -// These values record the result type of the node (as checked by NodeResultMask, above), 0 for no result. -#define NodeResultJS 0x1000 -#define NodeResultNumber 0x2000 -#define NodeResultInt32 0x3000 -#define NodeResultBoolean 0x4000 -#define NodeResultStorage 0x5000 +const char* arithNodeFlagsAsString(NodeFlags); // This macro defines a set of information about all known node types, used to populate NodeId, NodeType below. #define FOR_EACH_DFG_OP(macro) \ @@ -204,6 +158,7 @@ static inline const char* arithNodeFlagsAsString(ArithNodeFlags flags) /* Nodes for arithmetic operations. */\ macro(ArithAdd, NodeResultNumber) \ macro(ArithSub, NodeResultNumber) \ + macro(ArithNegate, NodeResultNumber) \ macro(ArithMul, NodeResultNumber) \ macro(ArithDiv, NodeResultNumber) \ macro(ArithMod, NodeResultNumber) \ @@ -291,12 +246,23 @@ static inline const char* arithNodeFlagsAsString(ArithNodeFlags flags) macro(ToPrimitive, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \ macro(StrCat, NodeResultJS | NodeMustGenerate | NodeHasVarArgs | NodeClobbersWorld) \ \ + /* Nodes used for activations. Activation support works by having it anchored at */\ + /* epilgoues via TearOffActivation, and all CreateActivation nodes kept alive by */\ + /* being threaded with each other. */\ + macro(CreateActivation, NodeResultJS) \ + macro(TearOffActivation, NodeMustGenerate) \ + \ + /* Nodes for creating functions. */\ + macro(NewFunctionNoCheck, NodeResultJS) \ + macro(NewFunction, NodeResultJS) \ + macro(NewFunctionExpression, NodeResultJS) \ + \ /* Block terminals. */\ - macro(Jump, NodeMustGenerate | NodeIsTerminal | NodeIsJump) \ - macro(Branch, NodeMustGenerate | NodeIsTerminal | NodeIsBranch) \ - macro(Return, NodeMustGenerate | NodeIsTerminal) \ - macro(Throw, NodeMustGenerate | NodeIsTerminal) \ - macro(ThrowReferenceError, NodeMustGenerate | NodeIsTerminal) \ + macro(Jump, NodeMustGenerate) \ + macro(Branch, NodeMustGenerate) \ + macro(Return, NodeMustGenerate) \ + macro(Throw, NodeMustGenerate) \ + macro(ThrowReferenceError, NodeMustGenerate) \ \ /* This is a pseudo-terminal. It means that execution should fall out of DFG at */\ /* this point, but execution does continue in the basic block - just in a */\ @@ -305,20 +271,25 @@ static inline const char* arithNodeFlagsAsString(ArithNodeFlags flags) // This enum generates a monotonically increasing id for all Node types, // and is used by the subsequent enum to fill out the id (as accessed via the NodeIdMask). -enum NodeId { -#define DFG_OP_ENUM(opcode, flags) opcode##_id, +enum NodeType { +#define DFG_OP_ENUM(opcode, flags) opcode, FOR_EACH_DFG_OP(DFG_OP_ENUM) #undef DFG_OP_ENUM - LastNodeId + LastNodeType }; -// Entries in this enum describe all Node types. -// The enum value contains a monotonically increasing id, a result type, and additional flags. -enum NodeType { -#define DFG_OP_ENUM(opcode, flags) opcode = opcode##_id | (flags), +// Specifies the default flags for each node. +inline NodeFlags defaultFlags(NodeType op) +{ + switch (op) { +#define DFG_OP_ENUM(opcode, flags) case opcode: return flags; FOR_EACH_DFG_OP(DFG_OP_ENUM) #undef DFG_OP_ENUM -}; + default: + ASSERT_NOT_REACHED(); + return 0; + } +} // This type used in passing an immediate argument to Node constructor; // distinguishes an immediate value (typically an index into a CodeBlock data structure - @@ -341,34 +312,32 @@ struct Node { // Construct a node with up to 3 children, no immediate value. Node(NodeType op, CodeOrigin codeOrigin, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode) - : op(op) - , codeOrigin(codeOrigin) + : codeOrigin(codeOrigin) , children(NodeReferenceBlob::Fixed, child1, child2, child3) , m_virtualRegister(InvalidVirtualRegister) , m_refCount(0) , m_prediction(PredictNone) { - ASSERT(!(op & NodeHasVarArgs)); - ASSERT(!hasArithNodeFlags()); + setOpAndDefaultFlags(op); + ASSERT(!(flags & NodeHasVarArgs)); } // Construct a node with up to 3 children and an immediate value. Node(NodeType op, CodeOrigin codeOrigin, OpInfo imm, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode) - : op(op) - , codeOrigin(codeOrigin) + : codeOrigin(codeOrigin) , children(NodeReferenceBlob::Fixed, child1, child2, child3) , m_virtualRegister(InvalidVirtualRegister) , m_refCount(0) , m_opInfo(imm.m_value) , m_prediction(PredictNone) { - ASSERT(!(op & NodeHasVarArgs)); + setOpAndDefaultFlags(op); + ASSERT(!(flags & NodeHasVarArgs)); } // Construct a node with up to 3 children and two immediate values. Node(NodeType op, CodeOrigin codeOrigin, OpInfo imm1, OpInfo imm2, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode) - : op(op) - , codeOrigin(codeOrigin) + : codeOrigin(codeOrigin) , children(NodeReferenceBlob::Fixed, child1, child2, child3) , m_virtualRegister(InvalidVirtualRegister) , m_refCount(0) @@ -376,13 +345,13 @@ struct Node { , m_opInfo2(safeCast<unsigned>(imm2.m_value)) , m_prediction(PredictNone) { - ASSERT(!(op & NodeHasVarArgs)); + setOpAndDefaultFlags(op); + ASSERT(!(flags & NodeHasVarArgs)); } // Construct a node with a variable number of children and two immediate values. Node(VarArgTag, NodeType op, CodeOrigin codeOrigin, OpInfo imm1, OpInfo imm2, unsigned firstChild, unsigned numChildren) - : op(op) - , codeOrigin(codeOrigin) + : codeOrigin(codeOrigin) , children(NodeReferenceBlob::Variable, firstChild, numChildren) , m_virtualRegister(InvalidVirtualRegister) , m_refCount(0) @@ -390,12 +359,19 @@ struct Node { , m_opInfo2(safeCast<unsigned>(imm2.m_value)) , m_prediction(PredictNone) { - ASSERT(op & NodeHasVarArgs); + setOpAndDefaultFlags(op); + ASSERT(flags & NodeHasVarArgs); + } + + void setOpAndDefaultFlags(NodeType op) + { + this->op = op; + flags = defaultFlags(op); } bool mustGenerate() { - return op & NodeMustGenerate; + return flags & NodeMustGenerate; } bool isConstant() @@ -520,6 +496,7 @@ struct Node { case UInt32ToNumber: case ArithAdd: case ArithSub: + case ArithNegate: case ArithMul: case ArithAbs: case ArithMin: @@ -533,44 +510,32 @@ struct Node { } } - ArithNodeFlags rawArithNodeFlags() - { - ASSERT(hasArithNodeFlags()); - return m_opInfo; - } - // This corrects the arithmetic node flags, so that irrelevant bits are // ignored. In particular, anything other than ArithMul does not need // to know if it can speculate on negative zero. - ArithNodeFlags arithNodeFlags() + NodeFlags arithNodeFlags() { - ArithNodeFlags result = rawArithNodeFlags(); + NodeFlags result = flags & NodeArithMask; if (op == ArithMul) return result; return result & ~NodeNeedsNegZero; } - ArithNodeFlags arithNodeFlagsForCompare() - { - if (hasArithNodeFlags()) - return arithNodeFlags(); - return 0; - } - - void setArithNodeFlag(ArithNodeFlags flags) + void setArithNodeFlag(NodeFlags newFlags) { - ASSERT(hasArithNodeFlags()); - m_opInfo = flags; + ASSERT(!(newFlags & ~NodeArithMask)); + + flags &= ~NodeArithMask; + flags |= newFlags; } - bool mergeArithNodeFlags(ArithNodeFlags flags) + bool mergeArithNodeFlags(NodeFlags newFlags) { - if (!hasArithNodeFlags()) + ASSERT(!(newFlags & ~NodeArithMask)); + newFlags = flags | newFlags; + if (newFlags == flags) return false; - ArithNodeFlags newFlags = m_opInfo | flags; - if (newFlags == m_opInfo) - return false; - m_opInfo = newFlags; + flags = newFlags; return true; } @@ -626,42 +591,51 @@ struct Node { bool hasResult() { - return op & NodeResultMask; + return flags & NodeResultMask; } bool hasInt32Result() { - return (op & NodeResultMask) == NodeResultInt32; + return (flags & NodeResultMask) == NodeResultInt32; } bool hasNumberResult() { - return (op & NodeResultMask) == NodeResultNumber; + return (flags & NodeResultMask) == NodeResultNumber; } bool hasJSResult() { - return (op & NodeResultMask) == NodeResultJS; + return (flags & NodeResultMask) == NodeResultJS; } bool hasBooleanResult() { - return (op & NodeResultMask) == NodeResultBoolean; + return (flags & NodeResultMask) == NodeResultBoolean; } bool isJump() { - return op & NodeIsJump; + return op == Jump; } bool isBranch() { - return op & NodeIsBranch; + return op == Branch; } bool isTerminal() { - return op & NodeIsTerminal; + switch (op) { + case Jump: + case Branch: + case Return: + case Throw: + case ThrowReferenceError: + return true; + default: + return false; + } } unsigned takenBytecodeOffsetDuringParsing() @@ -775,6 +749,30 @@ struct Node { unsigned storageAccessDataIndex() { + ASSERT(hasStorageAccessData()); + return m_opInfo; + } + + bool hasFunctionDeclIndex() + { + return op == NewFunction + || op == NewFunctionNoCheck; + } + + unsigned functionDeclIndex() + { + ASSERT(hasFunctionDeclIndex()); + return m_opInfo; + } + + bool hasFunctionExprIndex() + { + return op == NewFunctionExpression; + } + + unsigned functionExprIndex() + { + ASSERT(hasFunctionExprIndex()); return m_opInfo; } @@ -799,7 +797,7 @@ struct Node { bool shouldGenerate() { - return m_refCount && op != Phi && op != Flush; + return m_refCount; } unsigned refCount() @@ -834,7 +832,7 @@ struct Node { NodeUse child1() { - ASSERT(!(op & NodeHasVarArgs)); + ASSERT(!(flags & NodeHasVarArgs)); return children.child1(); } @@ -848,25 +846,25 @@ struct Node { NodeUse child2() { - ASSERT(!(op & NodeHasVarArgs)); + ASSERT(!(flags & NodeHasVarArgs)); return children.child2(); } NodeUse child3() { - ASSERT(!(op & NodeHasVarArgs)); + ASSERT(!(flags & NodeHasVarArgs)); return children.child3(); } unsigned firstChild() { - ASSERT(op & NodeHasVarArgs); + ASSERT(flags & NodeHasVarArgs); return children.firstChild(); } unsigned numChildren() { - ASSERT(op & NodeHasVarArgs); + ASSERT(flags & NodeHasVarArgs); return children.numChildren(); } @@ -1032,8 +1030,8 @@ struct Node { fprintf(out, ", @%u", child3().index()); } - // This enum value describes the type of the node. - NodeType op; + uint16_t op; // real type is NodeType + NodeFlags flags; // Used to look up exception handling information (currently implemented as a bytecode index). CodeOrigin codeOrigin; // References to up to 3 children, or links to a variable length set of children. diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp index a672234a3..bd45020d1 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp @@ -98,7 +98,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco m_jit.store32(scratch, &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.payload); m_jit.load32(scratchBuffer, scratch); } else if (exit.m_jsValueSource.hasKnownTag()) { - m_jit.store32(AssemblyHelpers::Imm32(exit.m_jsValueSource.tag()), &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.tag); + m_jit.store32(AssemblyHelpers::TrustedImm32(exit.m_jsValueSource.tag()), &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.tag); m_jit.store32(exit.m_jsValueSource.payloadGPR(), &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.payload); } else { m_jit.store32(exit.m_jsValueSource.tagGPR(), &bitwise_cast<EncodedValueDescriptor*>(bucket)->asBits.tag); @@ -562,32 +562,40 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // counter to 0; otherwise we set the counter to // counterValueForOptimizeAfterWarmUp(). - m_jit.add32(AssemblyHelpers::Imm32(1), AssemblyHelpers::AbsoluteAddress(&exit.m_count)); + m_jit.add32(AssemblyHelpers::TrustedImm32(1), AssemblyHelpers::AbsoluteAddress(&exit.m_count)); m_jit.move(AssemblyHelpers::TrustedImmPtr(m_jit.codeBlock()), GPRInfo::regT0); m_jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeFailCounter()), GPRInfo::regT2); m_jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeSuccessCounter()), GPRInfo::regT1); - m_jit.add32(AssemblyHelpers::Imm32(1), GPRInfo::regT2); - m_jit.add32(AssemblyHelpers::Imm32(-1), GPRInfo::regT1); + m_jit.add32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2); + m_jit.add32(AssemblyHelpers::TrustedImm32(-1), GPRInfo::regT1); m_jit.store32(GPRInfo::regT2, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeFailCounter())); m_jit.store32(GPRInfo::regT1, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeSuccessCounter())); m_jit.move(AssemblyHelpers::TrustedImmPtr(m_jit.baselineCodeBlock()), GPRInfo::regT0); - AssemblyHelpers::Jump fewFails = m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, AssemblyHelpers::Imm32(m_jit.codeBlock()->largeFailCountThreshold())); - m_jit.mul32(AssemblyHelpers::Imm32(Options::desiredSpeculativeSuccessFailRatio), GPRInfo::regT2, GPRInfo::regT2); + AssemblyHelpers::Jump fewFails = m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, AssemblyHelpers::TrustedImm32(m_jit.codeBlock()->largeFailCountThreshold())); + m_jit.mul32(AssemblyHelpers::TrustedImm32(Options::desiredSpeculativeSuccessFailRatio), GPRInfo::regT2, GPRInfo::regT2); AssemblyHelpers::Jump lowFailRate = m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, GPRInfo::regT1); // Reoptimize as soon as possible. - m_jit.store32(AssemblyHelpers::Imm32(Options::executionCounterValueForOptimizeNextInvocation), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::TrustedImm32(0), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::TrustedImm32(0), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); AssemblyHelpers::Jump doneAdjusting = m_jit.jump(); fewFails.link(&m_jit); lowFailRate.link(&m_jit); - m_jit.store32(AssemblyHelpers::Imm32(m_jit.baselineCodeBlock()->counterValueForOptimizeAfterLongWarmUp()), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + // Adjust the execution counter such that the target is to only optimize after a while. + int32_t targetValue = + ExecutionCounter::applyMemoryUsageHeuristicsAndConvertToInt( + m_jit.baselineCodeBlock()->counterValueForOptimizeAfterLongWarmUp(), + m_jit.baselineCodeBlock()); + m_jit.store32(AssemblyHelpers::TrustedImm32(-targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::TrustedImm32(targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); + m_jit.store32(AssemblyHelpers::TrustedImm32(ExecutionCounter::formattedTotalCount(targetValue)), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionTotalCount())); doneAdjusting.link(&m_jit); @@ -618,24 +626,24 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco GPRReg callerFrameGPR; if (inlineCallFrame->caller.inlineCallFrame) { - m_jit.add32(AssemblyHelpers::Imm32(inlineCallFrame->caller.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT3); + m_jit.add32(AssemblyHelpers::TrustedImm32(inlineCallFrame->caller.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT3); callerFrameGPR = GPRInfo::regT3; } else callerFrameGPR = GPRInfo::callFrameRegister; m_jit.storePtr(AssemblyHelpers::TrustedImmPtr(baselineCodeBlock), AssemblyHelpers::addressFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::CodeBlock))); - m_jit.store32(AssemblyHelpers::Imm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::ScopeChain))); + m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::ScopeChain))); m_jit.storePtr(AssemblyHelpers::TrustedImmPtr(inlineCallFrame->callee->scope()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::ScopeChain))); - m_jit.store32(AssemblyHelpers::Imm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::CallerFrame))); + m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::CallerFrame))); m_jit.storePtr(callerFrameGPR, AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::CallerFrame))); m_jit.storePtr(AssemblyHelpers::TrustedImmPtr(jumpTarget), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::ReturnPC))); - m_jit.store32(AssemblyHelpers::Imm32(inlineCallFrame->arguments.size()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::ArgumentCount))); - m_jit.store32(AssemblyHelpers::Imm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::Callee))); + m_jit.store32(AssemblyHelpers::TrustedImm32(inlineCallFrame->arguments.size()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::ArgumentCount))); + m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::Callee))); m_jit.storePtr(AssemblyHelpers::TrustedImmPtr(inlineCallFrame->callee.get()), AssemblyHelpers::payloadFor((VirtualRegister)(inlineCallFrame->stackOffset + RegisterFile::Callee))); } if (exit.m_codeOrigin.inlineCallFrame) - m_jit.addPtr(AssemblyHelpers::Imm32(exit.m_codeOrigin.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister); + m_jit.addPtr(AssemblyHelpers::TrustedImm32(exit.m_codeOrigin.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister); // 14) Jump into the corresponding baseline JIT code. diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp index f5e03973c..91a515c48 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp @@ -227,7 +227,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco break; case AlreadyInRegisterFileAsUnboxedInt32: - m_jit.store32(AssemblyHelpers::Imm32(static_cast<uint32_t>(TagTypeNumber >> 32)), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(exit.operandForIndex(index)))); + m_jit.store32(AssemblyHelpers::TrustedImm32(static_cast<uint32_t>(TagTypeNumber >> 32)), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(exit.operandForIndex(index)))); break; case UInt32InGPR: { @@ -541,32 +541,40 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // counter to 0; otherwise we set the counter to // counterValueForOptimizeAfterWarmUp(). - m_jit.add32(AssemblyHelpers::Imm32(1), AssemblyHelpers::AbsoluteAddress(&exit.m_count)); + m_jit.add32(AssemblyHelpers::TrustedImm32(1), AssemblyHelpers::AbsoluteAddress(&exit.m_count)); m_jit.move(AssemblyHelpers::TrustedImmPtr(m_jit.codeBlock()), GPRInfo::regT0); m_jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeFailCounter()), GPRInfo::regT2); m_jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeSuccessCounter()), GPRInfo::regT1); - m_jit.add32(AssemblyHelpers::Imm32(1), GPRInfo::regT2); - m_jit.add32(AssemblyHelpers::Imm32(-1), GPRInfo::regT1); + m_jit.add32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2); + m_jit.add32(AssemblyHelpers::TrustedImm32(-1), GPRInfo::regT1); m_jit.store32(GPRInfo::regT2, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeFailCounter())); m_jit.store32(GPRInfo::regT1, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeSuccessCounter())); m_jit.move(AssemblyHelpers::TrustedImmPtr(m_jit.baselineCodeBlock()), GPRInfo::regT0); - AssemblyHelpers::Jump fewFails = m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, AssemblyHelpers::Imm32(m_jit.codeBlock()->largeFailCountThreshold())); - m_jit.mul32(AssemblyHelpers::Imm32(Options::desiredSpeculativeSuccessFailRatio), GPRInfo::regT2, GPRInfo::regT2); + AssemblyHelpers::Jump fewFails = m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, AssemblyHelpers::TrustedImm32(m_jit.codeBlock()->largeFailCountThreshold())); + m_jit.mul32(AssemblyHelpers::TrustedImm32(Options::desiredSpeculativeSuccessFailRatio), GPRInfo::regT2, GPRInfo::regT2); AssemblyHelpers::Jump lowFailRate = m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, GPRInfo::regT1); // Reoptimize as soon as possible. - m_jit.store32(AssemblyHelpers::Imm32(Options::executionCounterValueForOptimizeNextInvocation), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::TrustedImm32(0), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::TrustedImm32(0), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); AssemblyHelpers::Jump doneAdjusting = m_jit.jump(); fewFails.link(&m_jit); lowFailRate.link(&m_jit); - m_jit.store32(AssemblyHelpers::Imm32(m_jit.baselineCodeBlock()->counterValueForOptimizeAfterLongWarmUp()), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + // Adjust the execution counter such that the target is to only optimize after a while. + int32_t targetValue = + ExecutionCounter::applyMemoryUsageHeuristicsAndConvertToInt( + m_jit.baselineCodeBlock()->counterValueForOptimizeAfterLongWarmUp(), + m_jit.baselineCodeBlock()); + m_jit.store32(AssemblyHelpers::TrustedImm32(-targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + m_jit.store32(AssemblyHelpers::TrustedImm32(targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); + m_jit.store32(AssemblyHelpers::TrustedImm32(ExecutionCounter::formattedTotalCount(targetValue)), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionTotalCount())); doneAdjusting.link(&m_jit); @@ -595,7 +603,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco GPRReg callerFrameGPR; if (inlineCallFrame->caller.inlineCallFrame) { - m_jit.addPtr(AssemblyHelpers::Imm32(inlineCallFrame->caller.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT3); + m_jit.addPtr(AssemblyHelpers::TrustedImm32(inlineCallFrame->caller.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister, GPRInfo::regT3); callerFrameGPR = GPRInfo::regT3; } else callerFrameGPR = GPRInfo::callFrameRegister; @@ -609,7 +617,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco } if (exit.m_codeOrigin.inlineCallFrame) - m_jit.addPtr(AssemblyHelpers::Imm32(exit.m_codeOrigin.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister); + m_jit.addPtr(AssemblyHelpers::TrustedImm32(exit.m_codeOrigin.inlineCallFrame->stackOffset * sizeof(EncodedJSValue)), GPRInfo::callFrameRegister); // 16) Jump into the corresponding baseline JIT code. diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 165a21416..304c54d95 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -31,10 +31,12 @@ #include "DFGRepatch.h" #include "HostCallReturnValue.h" #include "GetterSetter.h" -#include "InlineASM.h" +#include <wtf/InlineASM.h> #include "Interpreter.h" +#include "JSActivation.h" #include "JSByteArray.h" #include "JSGlobalData.h" +#include "JSStaticScopeObject.h" #include "Operations.h" #if ENABLE(DFG_JIT) @@ -144,6 +146,7 @@ FUNCTION_WRAPPER_WITH_RETURN_ADDRESS_EJCI(function) namespace JSC { namespace DFG { +template<bool strict> static inline void putByVal(ExecState* exec, JSValue baseValue, uint32_t index, JSValue value) { JSGlobalData* globalData = &exec->globalData(); @@ -155,7 +158,7 @@ static inline void putByVal(ExecState* exec, JSValue baseValue, uint32_t index, return; } - JSArray::putByIndex(array, exec, index, value); + JSArray::putByIndex(array, exec, index, value, strict); return; } @@ -173,7 +176,7 @@ static inline void putByVal(ExecState* exec, JSValue baseValue, uint32_t index, } } - baseValue.put(exec, index, value); + baseValue.putByIndex(exec, index, value, strict); } template<bool strict> @@ -187,7 +190,7 @@ ALWAYS_INLINE static void DFG_OPERATION operationPutByValInternal(ExecState* exe JSValue value = JSValue::decode(encodedValue); if (LIKELY(property.isUInt32())) { - putByVal(exec, baseValue, property.asUInt32(), value); + putByVal<strict>(exec, baseValue, property.asUInt32(), value); return; } @@ -195,7 +198,7 @@ ALWAYS_INLINE static void DFG_OPERATION operationPutByValInternal(ExecState* exe double propertyAsDouble = property.asDouble(); uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble); if (propertyAsDouble == propertyAsUInt32) { - putByVal(exec, baseValue, propertyAsUInt32, value); + putByVal<strict>(exec, baseValue, propertyAsUInt32, value); return; } } @@ -471,14 +474,24 @@ void DFG_OPERATION operationPutByValCellNonStrict(ExecState* exec, JSCell* cell, operationPutByValInternal<false>(exec, JSValue::encode(cell), encodedProperty, encodedValue); } -void DFG_OPERATION operationPutByValBeyondArrayBounds(ExecState* exec, JSArray* array, int32_t index, EncodedJSValue encodedValue) +void DFG_OPERATION operationPutByValBeyondArrayBoundsStrict(ExecState* exec, JSArray* array, int32_t index, EncodedJSValue encodedValue) +{ + JSGlobalData* globalData = &exec->globalData(); + NativeCallFrameTracer tracer(globalData, exec); + + // We should only get here if index is outside the existing vector. + ASSERT(!array->canSetIndex(index)); + JSArray::putByIndex(array, exec, index, JSValue::decode(encodedValue), true); +} + +void DFG_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState* exec, JSArray* array, int32_t index, EncodedJSValue encodedValue) { JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); // We should only get here if index is outside the existing vector. ASSERT(!array->canSetIndex(index)); - JSArray::putByIndex(array, exec, index, JSValue::decode(encodedValue)); + JSArray::putByIndex(array, exec, index, JSValue::decode(encodedValue), false); } EncodedJSValue DFG_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue encodedValue, JSArray* array) @@ -734,8 +747,11 @@ size_t DFG_OPERATION operationCompareStrictEq(ExecState* exec, EncodedJSValue en { JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); + + JSValue src1 = JSValue::decode(encodedOp1); + JSValue src2 = JSValue::decode(encodedOp2); - return JSValue::strictEqual(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); + return JSValue::strictEqual(exec, src1, src2); } static void* handleHostCall(ExecState* execCallee, JSValue callee, CodeSpecializationKind kind) @@ -974,6 +990,43 @@ EncodedJSValue DFG_OPERATION operationNewRegexp(ExecState* exec, void* regexpPtr return JSValue::encode(RegExpObject::create(exec->globalData(), exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->regExpStructure(), regexp)); } +JSCell* DFG_OPERATION operationCreateActivation(ExecState* exec) +{ + JSGlobalData& globalData = exec->globalData(); + JSActivation* activation = JSActivation::create( + globalData, exec, static_cast<FunctionExecutable*>(exec->codeBlock()->ownerExecutable())); + exec->setScopeChain(exec->scopeChain()->push(activation)); + return activation; +} + +void DFG_OPERATION operationTearOffActivation(ExecState* exec, JSCell* activation) +{ + ASSERT(activation); + ASSERT(activation->inherits(&JSActivation::s_info)); + static_cast<JSActivation*>(activation)->tearOff(exec->globalData()); +} + +JSCell* DFG_OPERATION operationNewFunction(ExecState* exec, JSCell* functionExecutable) +{ + ASSERT(functionExecutable->inherits(&FunctionExecutable::s_info)); + return static_cast<FunctionExecutable*>(functionExecutable)->make(exec, exec->scopeChain()); +} + +JSCell* DFG_OPERATION operationNewFunctionExpression(ExecState* exec, JSCell* functionExecutableAsCell) +{ + ASSERT(functionExecutableAsCell->inherits(&FunctionExecutable::s_info)); + FunctionExecutable* functionExecutable = + static_cast<FunctionExecutable*>(functionExecutableAsCell); + JSFunction *function = functionExecutable->make(exec, exec->scopeChain()); + if (!functionExecutable->name().isNull()) { + JSStaticScopeObject* functionScopeObject = + JSStaticScopeObject::create( + exec, functionExecutable->name(), function, ReadOnly | DontDelete); + function->setScope(exec->globalData(), function->scope()->push(functionScopeObject)); + } + return function; +} + DFGHandlerEncoded DFG_OPERATION lookupExceptionHandler(ExecState* exec, uint32_t callIndex) { JSGlobalData* globalData = &exec->globalData(); diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h index cdb88de27..4ca58d621 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.h +++ b/Source/JavaScriptCore/dfg/DFGOperations.h @@ -89,6 +89,7 @@ typedef void DFG_OPERATION (*V_DFGOperation_EAZJ)(ExecState*, JSArray*, int32_t, typedef double DFG_OPERATION (*D_DFGOperation_DD)(double, double); typedef double DFG_OPERATION (*D_DFGOperation_EJ)(ExecState*, EncodedJSValue); typedef void* DFG_OPERATION (*P_DFGOperation_E)(ExecState*); +typedef void DFG_OPERATION (V_DFGOperation_EC)(ExecState*, JSCell*); // These routines are provide callbacks out to C++ implementations of operations too complex to JIT. JSCell* DFG_OPERATION operationNewObject(ExecState*); @@ -118,7 +119,8 @@ void DFG_OPERATION operationPutByValStrict(ExecState*, EncodedJSValue encodedBas void DFG_OPERATION operationPutByValNonStrict(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedValue); void DFG_OPERATION operationPutByValCellStrict(ExecState*, JSCell*, EncodedJSValue encodedProperty, EncodedJSValue encodedValue); void DFG_OPERATION operationPutByValCellNonStrict(ExecState*, JSCell*, EncodedJSValue encodedProperty, EncodedJSValue encodedValue); -void DFG_OPERATION operationPutByValBeyondArrayBounds(ExecState*, JSArray*, int32_t index, EncodedJSValue encodedValue); +void DFG_OPERATION operationPutByValBeyondArrayBoundsStrict(ExecState*, JSArray*, int32_t index, EncodedJSValue encodedValue); +void DFG_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState*, JSArray*, int32_t index, EncodedJSValue encodedValue); EncodedJSValue DFG_OPERATION operationArrayPush(ExecState*, EncodedJSValue encodedValue, JSArray*); EncodedJSValue DFG_OPERATION operationArrayPop(ExecState*, JSArray*); void DFG_OPERATION operationPutByIdStrict(ExecState*, EncodedJSValue encodedValue, JSCell* base, Identifier*); @@ -145,6 +147,10 @@ void* DFG_OPERATION operationVirtualCall(ExecState*); void* DFG_OPERATION operationLinkCall(ExecState*); void* DFG_OPERATION operationVirtualConstruct(ExecState*); void* DFG_OPERATION operationLinkConstruct(ExecState*); +JSCell* DFG_OPERATION operationCreateActivation(ExecState*); +void DFG_OPERATION operationTearOffActivation(ExecState*, JSCell*); +JSCell* DFG_OPERATION operationNewFunction(ExecState*, JSCell*); +JSCell* DFG_OPERATION operationNewFunctionExpression(ExecState*, JSCell*); // This method is used to lookup an exception hander, keyed by faultLocation, which is // the return location from one of the calls out to one of the helper operations above. diff --git a/Source/JavaScriptCore/dfg/DFGPhase.cpp b/Source/JavaScriptCore/dfg/DFGPhase.cpp index bc1eabff4..bae12b1cc 100644 --- a/Source/JavaScriptCore/dfg/DFGPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPhase.cpp @@ -35,7 +35,7 @@ void Phase::beginPhase() { dataLog("Beginning DFG phase %s.\n", m_name); dataLog("Graph before %s:\n", m_name); - m_graph.dump(m_codeBlock); + m_graph.dump(); } void Phase::endPhase() diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index b4c9e075a..98bdaac06 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -105,7 +105,7 @@ private: if (!node.shouldGenerate()) return; - NodeType op = node.op; + NodeType op = static_cast<NodeType>(node.op); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" %s @%u: ", Graph::opName(op), m_compileIndex); @@ -209,6 +209,15 @@ private: break; } + case ArithNegate: + if (m_graph[node.child1()].prediction()) { + if (m_graph.negateShouldSpeculateInteger(node)) + changed |= mergePrediction(PredictInt32); + else + changed |= mergePrediction(PredictDouble); + } + break; + case ArithMul: case ArithMin: case ArithMax: @@ -397,6 +406,18 @@ private: break; } + case CreateActivation: { + changed |= setPrediction(PredictObjectOther); + break; + } + + case NewFunction: + case NewFunctionNoCheck: + case NewFunctionExpression: { + changed |= setPrediction(PredictFunction); + break; + } + case GetArrayLength: case GetByteArrayLength: case GetInt8ArrayLength: @@ -415,6 +436,9 @@ private: break; } + case Flush: + break; + #ifndef NDEBUG // These get ignored because they don't return anything. case PutScopedVar: @@ -424,7 +448,6 @@ private: case Return: case CheckHasInstance: case Phi: - case Flush: case Throw: case ThrowReferenceError: case ForceOSRExit: @@ -437,6 +460,7 @@ private: case CheckFunction: case PutStructure: case PutByOffset: + case TearOffActivation: break; // These gets ignored because it doesn't do anything. @@ -444,6 +468,10 @@ private: case InlineStart: case Nop: break; + + case LastNodeType: + ASSERT_NOT_REACHED(); + break; #else default: break; @@ -492,7 +520,7 @@ private: void vote(Node& node, VariableAccessData::Ballot ballot) { - if (node.op & NodeHasVarArgs) { + if (node.flags & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) vote(m_graph.m_varArgChildren[childIdx], ballot); return; @@ -586,8 +614,13 @@ private: break; } } - for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) - m_changed |= m_graph.m_variableAccessData[i].find()->tallyVotesForShouldUseDoubleFormat(); + for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) { + VariableAccessData* variableAccessData = m_graph.m_variableAccessData[i].find(); + if (operandIsArgument(variableAccessData->local()) + || m_graph.isCaptured(variableAccessData->local())) + continue; + m_changed |= variableAccessData->tallyVotesForShouldUseDoubleFormat(); + } } void fixupNode(Node& node) @@ -595,7 +628,7 @@ private: if (!node.shouldGenerate()) return; - NodeType op = node.op; + NodeType op = static_cast<NodeType>(node.op); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" %s @%u: ", Graph::opName(op), m_compileIndex); @@ -651,13 +684,16 @@ private: node.op = GetFloat64ArrayLength; else ASSERT_NOT_REACHED(); - m_graph.deref(m_compileIndex); // No longer MustGenerate + // No longer MustGenerate + ASSERT(node.flags & NodeMustGenerate); + node.flags &= ~NodeMustGenerate; + m_graph.deref(m_compileIndex); break; } case GetIndexedPropertyStorage: { PredictedType basePrediction = m_graph[node.child2()].prediction(); if (!(basePrediction & PredictInt32) && basePrediction) { - node.op = Nop; + node.setOpAndDefaultFlags(Nop); m_graph.clearAndDerefChild1(node); m_graph.clearAndDerefChild2(node); m_graph.clearAndDerefChild3(node); diff --git a/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp b/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp new file mode 100644 index 000000000..fb30de742 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DFGRedundantPhiEliminationPhase.h" + +#if ENABLE(DFG_JIT) + +#include "DFGGraph.h" + +namespace JSC { namespace DFG { + +class RedundantPhiEliminationPhase : public Phase { +public: + RedundantPhiEliminationPhase(Graph& graph) + : Phase(graph, "redundant phi elimination") + { + } + + void run() + { + bool changed = false; + do { + changed = fixupPhis(); + } while (changed); + + updateBlockVariableInformation(); + + // Update the Phi references from non-Phi nodes, e.g., the GetLocals. + for (NodeIndex index = 0; index < m_graph.size(); ++index) { + Node& node = m_graph[index]; + + if (!node.shouldGenerate()) + continue; + + switch (node.op) { + case GetLocal: + replacePhiChild(node, 0); + break; + default: + break; + } + } + + } + +private: + NodeIndex getRedundantReplacement(NodeIndex phi) + { + NodeIndex child1 = m_graph[phi].child1().indexUnchecked(); + NodeIndex candidate = child1 == phi ? NoNode : child1; + + NodeIndex child2 = m_graph[phi].child2().indexUnchecked(); + if (candidate != NoNode) { + if (child2 != NoNode && child2 != candidate && child2 != phi) + return NoNode; + } else if (child2 != phi) + candidate = child2; + + NodeIndex child3 = m_graph[phi].child3().indexUnchecked(); + if (candidate != NoNode) { + if (child3 != NoNode && child3 != candidate && child3 != phi) + return NoNode; + } else if (child3 != phi) + candidate = child3; + + return candidate; + } + + bool replacePhiChild(Node& node, unsigned childIndex) + { + ASSERT(childIndex < 3); + + bool replaced = false; + NodeIndex child = node.children.child(childIndex).indexUnchecked(); + if (child != NoNode && m_graph[child].op == Phi) { + NodeIndex childReplacement = getRedundantReplacement(child); + if (childReplacement != NoNode) { + node.children.child(childIndex).setIndex(childReplacement); + replaced = true; + if (node.refCount()) { + m_graph[childReplacement].ref(); + m_graph.deref(child); + } + } + } + return replaced; + } + + bool fixupPhis() + { + bool changed = false; + + for (BlockIndex block = 0; block < m_graph.m_blocks.size(); ++block) { + Vector<NodeIndex>& phis = m_graph.m_blocks[block]->phis; + + for (size_t i = 0; i < phis.size(); ++i) { + NodeIndex phi = phis[i]; + Node& phiNode = m_graph[phi]; + + changed |= (replacePhiChild(phiNode, 0) && phiNode.refCount()); + changed |= (replacePhiChild(phiNode, 1) && phiNode.refCount()); + changed |= (replacePhiChild(phiNode, 2) && phiNode.refCount()); + } + } + + return changed; + } + + void updateBlockVariableInformation() + { + // Redundant Phi nodes are eliminated, we need to update + // the variable information if it references them. + for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { + BasicBlock* basicBlock = m_graph.m_blocks[blockIndex].get(); + + for (size_t arg = 0; arg < basicBlock->variablesAtHead.numberOfArguments(); ++arg) { + NodeIndex nodeIndex = basicBlock->variablesAtHead.argument(arg); + if (nodeIndex != NoNode && m_graph[nodeIndex].op == Phi && !m_graph[nodeIndex].refCount()) { + NodeIndex replacement = getRedundantReplacement(nodeIndex); + if (replacement != NoNode) { + // This argument must be unused in this block. + ASSERT(basicBlock->variablesAtTail.argument(arg) == nodeIndex); + basicBlock->variablesAtHead.argument(arg) = replacement; + basicBlock->variablesAtTail.argument(arg) = replacement; + } + } + } + + for (size_t local = 0; local < basicBlock->variablesAtHead.numberOfLocals(); ++local) { + NodeIndex nodeIndex = basicBlock->variablesAtHead.local(local); + if (nodeIndex != NoNode && m_graph[nodeIndex].op == Phi && !m_graph[nodeIndex].refCount()) { + NodeIndex replacement = getRedundantReplacement(nodeIndex); + if (replacement != NoNode) { + // This local variable must be unused in this block. + ASSERT(basicBlock->variablesAtTail.local(local) == nodeIndex); + basicBlock->variablesAtHead.local(local) = replacement; + basicBlock->variablesAtTail.local(local) = replacement; + } + } + } + } + } + +}; + +void performRedundantPhiElimination(Graph& graph) +{ + runPhase<RedundantPhiEliminationPhase>(graph); +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) diff --git a/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.h b/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.h new file mode 100644 index 000000000..202ab4441 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DFGRedundantPhiEliminationPhase_h +#define DFGRedundantPhiEliminationPhase_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +#include "DFGPhase.h" + +namespace JSC { namespace DFG { + +class Graph; + +// We inserted many can-be-redundant Phi nodes when building the graph. +// This phase will just remove them. + +void performRedundantPhiElimination(Graph&); + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGRedundantPhiEliminationPhase_h + diff --git a/Source/JavaScriptCore/dfg/DFGScoreBoard.h b/Source/JavaScriptCore/dfg/DFGScoreBoard.h index 912b3e8fd..140de185b 100644 --- a/Source/JavaScriptCore/dfg/DFGScoreBoard.h +++ b/Source/JavaScriptCore/dfg/DFGScoreBoard.h @@ -58,14 +58,27 @@ public: } } -#if DFG_ENABLE(CONSISTENCY_CHECK) ~ScoreBoard() { - // For every entry in the used list the use count of the virtual register should be zero. - for (size_t i = 0; i < m_free.size(); ++i) - ASSERT(!m_used[i] || m_used[i] == max()); + assertClear(); } + + void assertClear() + { +#if !ASSERT_DISABLED + // For every entry in the used list the use count of the virtual register should be zero, or max, due to it being a preserved local. + for (size_t i = 0; i < m_used.size(); ++i) + ASSERT(!m_used[i] || m_used[i] == max()); + // For every entry in the free list, the use count should be zero. + for (size_t i = 0; i < m_free.size(); ++i) + ASSERT(!m_used[m_free[i]]); + // There must not be duplicates in the free list. + for (size_t i = 0; i < m_free.size(); ++i) { + for (size_t j = i + 1; j < m_free.size(); ++j) + ASSERT(m_free[i] != m_free[j]); + } #endif + } VirtualRegister allocate() { @@ -99,6 +112,9 @@ public: uint32_t index = node.virtualRegister(); ASSERT(m_used[index] != max()); if (node.refCount() == ++m_used[index]) { +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + dataLog(" Freeing virtual register %u.", index); +#endif // If the use count in the scoreboard reaches the use count for the node, // then this was its last use; the virtual register is now free. // Clear the use count & add to the free list. diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 8578337f5..7bcb44576 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -83,7 +83,7 @@ GPRReg SpeculativeJIT::fillStorage(NodeIndex nodeIndex) void SpeculativeJIT::useChildren(Node& node) { - if (node.op & NodeHasVarArgs) { + if (node.flags & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) use(m_jit.graph().m_varArgChildren[childIdx]); } else { @@ -365,12 +365,15 @@ void SpeculativeJIT::writeBarrier(JSCell* owner, GPRReg valueGPR, NodeUse valueU bool SpeculativeJIT::nonSpeculativeCompare(Node& node, MacroAssembler::RelationalCondition cond, S_DFGOperation_EJJ helperFunction) { - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + ASSERT(node.adjustedRefCount() == 1); nonSpeculativePeepholeBranch(node, branchNodeIndex, cond, helperFunction); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; @@ -383,15 +386,15 @@ bool SpeculativeJIT::nonSpeculativeCompare(Node& node, MacroAssembler::Relationa bool SpeculativeJIT::nonSpeculativeStrictEq(Node& node, bool invert) { - if (!invert && (isKnownNumeric(node.child1().index()) || isKnownNumeric(node.child2().index()))) - return nonSpeculativeCompare(node, MacroAssembler::Equal, operationCompareStrictEq); - - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + ASSERT(node.adjustedRefCount() == 1); nonSpeculativePeepholeStrictEq(node, branchNodeIndex, invert); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; @@ -765,7 +768,6 @@ FPRTemporary::FPRTemporary(SpeculativeJIT* jit, JSValueOperand& op1) } #endif -#ifndef NDEBUG void ValueSource::dump(FILE* out) const { switch (kind()) { @@ -792,7 +794,6 @@ void ValueSource::dump(FILE* out) const break; } } -#endif void SpeculativeJIT::compilePeepHoleDoubleBranch(Node& node, NodeIndex branchNodeIndex, JITCompiler::DoubleCondition condition) { @@ -873,8 +874,10 @@ void SpeculativeJIT::compilePeepHoleIntegerBranch(Node& node, NodeIndex branchNo bool SpeculativeJIT::compilePeepHoleBranch(Node& node, MacroAssembler::RelationalCondition condition, MacroAssembler::DoubleCondition doubleCondition, S_DFGOperation_EJJ operation) { // Fused compare & branch. - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + // detectPeepHoleBranch currently only permits the branch to be the very next node, // so can be no intervening nodes to also reference the compare. ASSERT(node.adjustedRefCount() == 1); @@ -898,6 +901,7 @@ bool SpeculativeJIT::compilePeepHoleBranch(Node& node, MacroAssembler::Relationa } else nonSpeculativePeepholeBranch(node, branchNodeIndex, condition, operation); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; } @@ -915,12 +919,9 @@ void SpeculativeJIT::compileMovHint(Node& node) void SpeculativeJIT::compile(BasicBlock& block) { ASSERT(m_compileOkay); - ASSERT(m_compileIndex == block.begin); - if (!block.isReachable) { - m_compileIndex = block.end; + if (!block.isReachable) return; - } m_blockHeads[m_block] = m_jit.label(); #if DFG_ENABLE(JIT_BREAK_ON_EVERY_BLOCK) @@ -930,7 +931,7 @@ void SpeculativeJIT::compile(BasicBlock& block) ASSERT(m_arguments.size() == block.variablesAtHead.numberOfArguments()); for (size_t i = 0; i < m_arguments.size(); ++i) { NodeIndex nodeIndex = block.variablesAtHead.argument(i); - if (nodeIndex == NoNode) + if (nodeIndex == NoNode || m_jit.graph().argumentIsCaptured(i)) m_arguments[i] = ValueSource(ValueInRegisterFile); else m_arguments[i] = ValueSource::forPrediction(at(nodeIndex).variableAccessData()->prediction()); @@ -942,7 +943,7 @@ void SpeculativeJIT::compile(BasicBlock& block) ASSERT(m_variables.size() == block.variablesAtHead.numberOfLocals()); for (size_t i = 0; i < m_variables.size(); ++i) { NodeIndex nodeIndex = block.variablesAtHead.local(i); - if (nodeIndex == NoNode) + if (nodeIndex == NoNode || m_jit.graph().localIsCaptured(i)) m_variables[i] = ValueSource(ValueInRegisterFile); else if (at(nodeIndex).variableAccessData()->shouldUseDoubleFormat()) m_variables[i] = ValueSource(DoubleInRegisterFile); @@ -955,12 +956,13 @@ void SpeculativeJIT::compile(BasicBlock& block) if (DFG_ENABLE_EDGE_CODE_VERIFICATION) { JITCompiler::Jump verificationSucceeded = - m_jit.branch32(JITCompiler::Equal, GPRInfo::regT0, Imm32(m_block)); + m_jit.branch32(JITCompiler::Equal, GPRInfo::regT0, TrustedImm32(m_block)); m_jit.breakpoint(); verificationSucceeded.link(&m_jit); } - for (; m_compileIndex < block.end; ++m_compileIndex) { + for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) { + m_compileIndex = block[m_indexInBlock]; Node& node = at(m_compileIndex); m_codeOriginForOSR = node.codeOrigin; if (!node.shouldGenerate()) { @@ -1005,7 +1007,6 @@ void SpeculativeJIT::compile(BasicBlock& block) compile(node); if (!m_compileOkay) { m_compileOkay = true; - m_compileIndex = block.end; clearGenerationInfo(); return; } @@ -1045,7 +1046,7 @@ void SpeculativeJIT::compile(BasicBlock& block) #endif // Make sure that the abstract state is rematerialized for the next node. - m_state.execute(m_compileIndex); + m_state.execute(m_indexInBlock); if (node.shouldGenerate()) checkConsistency(); @@ -1232,7 +1233,7 @@ bool SpeculativeJIT::compile() checkArgumentTypes(); if (DFG_ENABLE_EDGE_CODE_VERIFICATION) - m_jit.move(Imm32(0), GPRInfo::regT0); + m_jit.move(TrustedImm32(0), GPRInfo::regT0); ASSERT(!m_compileIndex); for (m_block = 0; m_block < m_jit.graph().m_blocks.size(); ++m_block) @@ -1258,7 +1259,7 @@ void SpeculativeJIT::createOSREntries() } m_osrEntryHeads.append(m_jit.label()); - m_jit.move(Imm32(blockIndex), GPRInfo::regT0); + m_jit.move(TrustedImm32(blockIndex), GPRInfo::regT0); m_jit.jump().linkTo(m_blockHeads[blockIndex], &m_jit); } } @@ -2318,8 +2319,14 @@ void SpeculativeJIT::compileArithSub(Node& node) if (nodeCanTruncateInteger(node.arithNodeFlags())) { m_jit.move(op1.gpr(), result.gpr()); m_jit.sub32(Imm32(imm2), result.gpr()); - } else + } else { +#if ENABLE(JIT_CONSTANT_BLINDING) + GPRTemporary scratch(this); + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr(), scratch.gpr())); +#else speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr())); +#endif + } integerResult(result.gpr(), m_compileIndex); return; @@ -2365,6 +2372,34 @@ void SpeculativeJIT::compileArithSub(Node& node) doubleResult(result.fpr(), m_compileIndex); } +void SpeculativeJIT::compileArithNegate(Node& node) +{ + if (m_jit.graph().negateShouldSpeculateInteger(node)) { + SpeculateIntegerOperand op1(this, node.child1()); + GPRTemporary result(this); + + m_jit.move(op1.gpr(), result.gpr()); + + if (nodeCanTruncateInteger(node.arithNodeFlags())) + m_jit.neg32(result.gpr()); + else { + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchNeg32(MacroAssembler::Overflow, result.gpr())); + if (!nodeCanIgnoreNegativeZero(node.arithNodeFlags())) + speculationCheck(Overflow, JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, result.gpr())); + } + + integerResult(result.gpr(), m_compileIndex); + return; + } + + SpeculateDoubleOperand op1(this, node.child1()); + FPRTemporary result(this); + + m_jit.negateDouble(op1.fpr(), result.fpr()); + + doubleResult(result.fpr(), m_compileIndex); +} + void SpeculativeJIT::compileArithMul(Node& node) { if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { @@ -2456,8 +2491,9 @@ bool SpeculativeJIT::compileStrictEqForConstant(Node& node, NodeUse value, JSVal { JSValueOperand op1(this, value); - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); Node& branchNode = at(branchNodeIndex); BlockIndex taken = branchNode.takenBlockIndex(); BlockIndex notTaken = branchNode.notTakenBlockIndex(); @@ -2493,6 +2529,7 @@ bool SpeculativeJIT::compileStrictEqForConstant(Node& node, NodeUse value, JSVal use(node.child1()); use(node.child2()); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; } @@ -2504,18 +2541,18 @@ bool SpeculativeJIT::compileStrictEqForConstant(Node& node, NodeUse value, JSVal GPRReg resultGPR = result.gpr(); m_jit.move(MacroAssembler::TrustedImmPtr(bitwise_cast<void*>(ValueFalse)), resultGPR); MacroAssembler::Jump notEqual = m_jit.branchPtr(MacroAssembler::NotEqual, op1GPR, MacroAssembler::TrustedImmPtr(bitwise_cast<void*>(JSValue::encode(constant)))); - m_jit.or32(MacroAssembler::Imm32(1), resultGPR); + m_jit.or32(MacroAssembler::TrustedImm32(1), resultGPR); notEqual.link(&m_jit); jsValueResult(resultGPR, m_compileIndex, DataFormatJSBoolean); #else GPRReg op1PayloadGPR = op1.payloadGPR(); GPRReg op1TagGPR = op1.tagGPR(); GPRReg resultGPR = result.gpr(); - m_jit.move(Imm32(0), resultGPR); + m_jit.move(TrustedImm32(0), resultGPR); MacroAssembler::JumpList notEqual; notEqual.append(m_jit.branch32(MacroAssembler::NotEqual, op1TagGPR, MacroAssembler::Imm32(constant.tag()))); notEqual.append(m_jit.branch32(MacroAssembler::NotEqual, op1PayloadGPR, MacroAssembler::Imm32(constant.payload()))); - m_jit.move(Imm32(1), resultGPR); + m_jit.move(TrustedImm32(1), resultGPR); notEqual.link(&m_jit); booleanResult(resultGPR, m_compileIndex); #endif @@ -2543,11 +2580,13 @@ bool SpeculativeJIT::compileStrictEq(Node& node) // 2) If the operands are predicted integer, do an integer comparison. if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))) { - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); compilePeepHoleIntegerBranch(node, branchNodeIndex, MacroAssembler::Equal); use(node.child1()); use(node.child2()); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; } @@ -2558,11 +2597,13 @@ bool SpeculativeJIT::compileStrictEq(Node& node) // 3) If the operands are predicted double, do a double comparison. if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) { - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); compilePeepHoleDoubleBranch(node, branchNodeIndex, MacroAssembler::DoubleEqual); use(node.child1()); use(node.child2()); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; } @@ -2574,11 +2615,13 @@ bool SpeculativeJIT::compileStrictEq(Node& node) // or array comparison. if (Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) { - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); compilePeepHoleObjectEquality(node, branchNodeIndex, &JSFinalObject::s_info, isFinalObjectPrediction); use(node.child1()); use(node.child2()); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; } @@ -2587,11 +2630,13 @@ bool SpeculativeJIT::compileStrictEq(Node& node) } if (Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) { - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); compilePeepHoleObjectEquality(node, branchNodeIndex, &JSArray::s_info, isArrayPrediction); use(node.child1()); use(node.child2()); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; } @@ -2691,6 +2736,28 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node) storageResult(storageReg, m_compileIndex); } +void SpeculativeJIT::compileNewFunctionNoCheck(Node& node) +{ + GPRResult result(this); + GPRReg resultGPR = result.gpr(); + flushRegisters(); + callOperation( + operationNewFunction, resultGPR, m_jit.codeBlock()->functionDecl(node.functionDeclIndex())); + cellResult(resultGPR, m_compileIndex); +} + +void SpeculativeJIT::compileNewFunctionExpression(Node& node) +{ + GPRResult result(this); + GPRReg resultGPR = result.gpr(); + flushRegisters(); + callOperation( + operationNewFunctionExpression, + resultGPR, + m_jit.codeBlock()->functionExpr(node.functionExprIndex())); + cellResult(resultGPR, m_compileIndex); +} + } } // namespace JSC::DFG #endif diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 5d95b064f..1744a03f3 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -104,9 +104,7 @@ public: return m_nodeIndex; } -#ifndef NDEBUG void dump(FILE* out) const; -#endif private: static NodeIndex nodeIndexFromKind(ValueSourceKind kind) @@ -454,7 +452,7 @@ private: ASSERT(info.gpr() == target); if (node.hasConstant()) { ASSERT(isBooleanConstant(nodeIndex)); - m_jit.move(Imm32(valueOfBooleanConstant(nodeIndex)), target); + m_jit.move(TrustedImm32(valueOfBooleanConstant(nodeIndex)), target); } else m_jit.load32(JITCompiler::payloadFor(spillMe), target); #endif @@ -466,7 +464,7 @@ private: if (node.hasConstant()) { JSValue value = valueOfJSConstant(nodeIndex); ASSERT(value.isCell()); - m_jit.move(ImmPtr(value.asCell()), target); + m_jit.move(TrustedImmPtr(value.asCell()), target); } else m_jit.loadPtr(JITCompiler::payloadFor(spillMe), target); return; @@ -481,9 +479,12 @@ private: ASSERT(registerFormat & DataFormatJS); #if USE(JSVALUE64) ASSERT(info.gpr() == target); - if (node.hasConstant()) - m_jit.move(valueOfJSConstantAsImmPtr(nodeIndex), target); - else if (info.spillFormat() == DataFormatInteger) { + if (node.hasConstant()) { + if (valueOfJSConstant(nodeIndex).isCell()) + m_jit.move(valueOfJSConstantAsImmPtr(nodeIndex).asTrustedImmPtr(), target); + else + m_jit.move(valueOfJSConstantAsImmPtr(nodeIndex), target); + } else if (info.spillFormat() == DataFormatInteger) { ASSERT(registerFormat == DataFormatJSInteger); m_jit.load32(JITCompiler::payloadFor(spillMe), target); m_jit.orPtr(GPRInfo::tagTypeNumberRegister, target); @@ -875,20 +876,21 @@ private: } } - // Returns the node index of the branch node if peephole is okay, NoNode otherwise. - NodeIndex detectPeepHoleBranch() + // Returns the index of the branch node if peephole is okay, UINT_MAX otherwise. + unsigned detectPeepHoleBranch() { - NodeIndex lastNodeIndex = m_jit.graph().m_blocks[m_block]->end - 1; + BasicBlock* block = m_jit.graph().m_blocks[m_block].get(); // Check that no intervening nodes will be generated. - for (NodeIndex index = m_compileIndex + 1; index < lastNodeIndex; ++index) { - if (at(index).shouldGenerate()) - return NoNode; + for (unsigned index = m_indexInBlock + 1; index < block->size() - 1; ++index) { + NodeIndex nodeIndex = block->at(index); + if (at(nodeIndex).shouldGenerate()) + return UINT_MAX; } // Check if the lastNode is a branch on this node. - Node& lastNode = at(lastNodeIndex); - return lastNode.op == Branch && lastNode.child1().index() == m_compileIndex ? lastNodeIndex : NoNode; + Node& lastNode = at(block->last()); + return lastNode.op == Branch && lastNode.child1().index() == m_compileIndex ? block->size() - 1 : UINT_MAX; } void nonSpeculativeValueToNumber(Node&); @@ -1124,7 +1126,7 @@ private: } JITCompiler::Call callOperation(J_DFGOperation_ESS operation, GPRReg result, int startConstant, int numConstants) { - m_jit.setupArgumentsWithExecState(Imm32(startConstant), Imm32(numConstants)); + m_jit.setupArgumentsWithExecState(TrustedImm32(startConstant), TrustedImm32(numConstants)); return appendCallWithExceptionCheckSetResult(operation, result); } JITCompiler::Call callOperation(J_DFGOperation_EPP operation, GPRReg result, GPRReg arg1, void* pointer) @@ -1162,6 +1164,11 @@ private: m_jit.setupArgumentsWithExecState(arg1); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(C_DFGOperation_EC operation, GPRReg result, JSCell* cell) + { + m_jit.setupArgumentsWithExecState(TrustedImmPtr(cell)); + return appendCallWithExceptionCheckSetResult(operation, result); + } JITCompiler::Call callOperation(C_DFGOperation_ECC operation, GPRReg result, GPRReg arg1, JSCell* cell) { m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(cell)); @@ -1182,14 +1189,14 @@ private: m_jit.setupArgumentsWithExecState(arg1, arg2); return appendCallWithExceptionCheckSetResult(operation, result); } - JITCompiler::Call callOperation(J_DFGOperation_EJJ operation, GPRReg result, GPRReg arg1, MacroAssembler::Imm32 imm) + JITCompiler::Call callOperation(J_DFGOperation_EJJ operation, GPRReg result, GPRReg arg1, MacroAssembler::TrustedImm32 imm) { - m_jit.setupArgumentsWithExecState(arg1, MacroAssembler::ImmPtr(static_cast<const void*>(JSValue::encode(jsNumber(imm.m_value))))); + m_jit.setupArgumentsWithExecState(arg1, MacroAssembler::TrustedImmPtr(static_cast<const void*>(JSValue::encode(jsNumber(imm.m_value))))); return appendCallWithExceptionCheckSetResult(operation, result); } - JITCompiler::Call callOperation(J_DFGOperation_EJJ operation, GPRReg result, MacroAssembler::Imm32 imm, GPRReg arg2) + JITCompiler::Call callOperation(J_DFGOperation_EJJ operation, GPRReg result, MacroAssembler::TrustedImm32 imm, GPRReg arg2) { - m_jit.setupArgumentsWithExecState(MacroAssembler::ImmPtr(static_cast<const void*>(JSValue::encode(jsNumber(imm.m_value)))), arg2); + m_jit.setupArgumentsWithExecState(MacroAssembler::TrustedImmPtr(static_cast<const void*>(JSValue::encode(jsNumber(imm.m_value)))), arg2); return appendCallWithExceptionCheckSetResult(operation, result); } JITCompiler::Call callOperation(J_DFGOperation_ECJ operation, GPRReg result, GPRReg arg1, GPRReg arg2) @@ -1197,6 +1204,11 @@ private: m_jit.setupArgumentsWithExecState(arg1, arg2); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(V_DFGOperation_EC operation, GPRReg arg1) + { + m_jit.setupArgumentsWithExecState(arg1); + return appendCallWithExceptionCheck(operation); + } JITCompiler::Call callOperation(V_DFGOperation_EJPP operation, GPRReg arg1, GPRReg arg2, void* pointer) { m_jit.setupArgumentsWithExecState(arg1, arg2, TrustedImmPtr(pointer)); @@ -1287,7 +1299,7 @@ private: } JITCompiler::Call callOperation(J_DFGOperation_EJP operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload, void* pointer) { - m_jit.setupArgumentsWithExecState(arg1Payload, arg1Tag, ImmPtr(pointer)); + m_jit.setupArgumentsWithExecState(arg1Payload, arg1Tag, TrustedImmPtr(pointer)); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); } JITCompiler::Call callOperation(J_DFGOperation_EJP operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2) @@ -1330,6 +1342,11 @@ private: m_jit.setupArgumentsWithExecState(arg1); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(C_DFGOperation_EC operation, GPRReg result, JSCell* cell) + { + m_jit.setupArgumentsWithExecState(TrustedImmPtr(cell)); + return appendCallWithExceptionCheckSetResult(operation, result); + } JITCompiler::Call callOperation(C_DFGOperation_ECC operation, GPRReg result, GPRReg arg1, JSCell* cell) { m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(cell)); @@ -1350,12 +1367,12 @@ private: m_jit.setupArgumentsWithExecState(arg1Payload, arg1Tag, arg2Payload, arg2Tag); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); } - JITCompiler::Call callOperation(J_DFGOperation_EJJ operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload, MacroAssembler::Imm32 imm) + JITCompiler::Call callOperation(J_DFGOperation_EJJ operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload, MacroAssembler::TrustedImm32 imm) { m_jit.setupArgumentsWithExecState(arg1Payload, arg1Tag, imm, TrustedImm32(JSValue::Int32Tag)); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); } - JITCompiler::Call callOperation(J_DFGOperation_EJJ operation, GPRReg resultTag, GPRReg resultPayload, MacroAssembler::Imm32 imm, GPRReg arg2Tag, GPRReg arg2Payload) + JITCompiler::Call callOperation(J_DFGOperation_EJJ operation, GPRReg resultTag, GPRReg resultPayload, MacroAssembler::TrustedImm32 imm, GPRReg arg2Tag, GPRReg arg2Payload) { m_jit.setupArgumentsWithExecState(imm, TrustedImm32(JSValue::Int32Tag), arg2Payload, arg2Tag); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); @@ -1365,6 +1382,11 @@ private: m_jit.setupArgumentsWithExecState(arg1, arg2Payload, arg2Tag); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); } + JITCompiler::Call callOperation(V_DFGOperation_EC operation, GPRReg arg1) + { + m_jit.setupArgumentsWithExecState(arg1); + return appendCallWithExceptionCheck(operation); + } JITCompiler::Call callOperation(V_DFGOperation_EJPP operation, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2, void* pointer) { m_jit.setupArgumentsWithExecState(arg1Payload, arg1Tag, arg2, TrustedImmPtr(pointer)); @@ -1620,7 +1642,7 @@ private: { if (!DFG_ENABLE_EDGE_CODE_VERIFICATION) return; - m_jit.move(Imm32(destination), GPRInfo::regT0); + m_jit.move(TrustedImm32(destination), GPRInfo::regT0); } void addBranch(const MacroAssembler::Jump& jump, BlockIndex destination) @@ -1693,6 +1715,7 @@ private: void compilePutByValForByteArray(GPRReg base, GPRReg property, Node&); void compileAdd(Node&); void compileArithSub(Node&); + void compileArithNegate(Node&); void compileArithMul(Node&); void compileArithMod(Node&); void compileSoftModulo(Node&); @@ -1715,14 +1738,18 @@ private: void compilePutByValForIntTypedArray(const TypedArrayDescriptor&, GPRReg base, GPRReg property, Node&, size_t elementSize, TypedArraySpeculationRequirements, TypedArraySignedness, TypedArrayRounding = TruncateRounding); void compileGetByValOnFloatTypedArray(const TypedArrayDescriptor&, Node&, size_t elementSize, TypedArraySpeculationRequirements); void compilePutByValForFloatTypedArray(const TypedArrayDescriptor&, GPRReg base, GPRReg property, Node&, size_t elementSize, TypedArraySpeculationRequirements); + void compileNewFunctionNoCheck(Node& node); + void compileNewFunctionExpression(Node& node); - // It is acceptable to have structure be equal to scratch, so long as you're fine - // with the structure GPR being clobbered. - template<typename T> - void emitAllocateJSFinalObject(T structure, GPRReg resultGPR, GPRReg scratchGPR, MacroAssembler::JumpList& slowPath) + template <typename ClassType, bool destructor, typename StructureType> + void emitAllocateBasicJSObject(StructureType structure, GPRReg resultGPR, GPRReg scratchGPR, MacroAssembler::JumpList& slowPath) { - MarkedAllocator* allocator = &m_jit.globalData()->heap.allocatorForObjectWithoutDestructor(sizeof(JSFinalObject)); - + MarkedAllocator* allocator = 0; + if (destructor) + allocator = &m_jit.globalData()->heap.allocatorForObjectWithDestructor(sizeof(ClassType)); + else + allocator = &m_jit.globalData()->heap.allocatorForObjectWithoutDestructor(sizeof(ClassType)); + m_jit.loadPtr(&allocator->m_firstFreeCell, resultGPR); slowPath.append(m_jit.branchTestPtr(MacroAssembler::Zero, resultGPR)); @@ -1738,14 +1765,22 @@ private: m_jit.storePtr(scratchGPR, &allocator->m_firstFreeCell); // Initialize the object's classInfo pointer - m_jit.storePtr(MacroAssembler::TrustedImmPtr(&JSFinalObject::s_info), MacroAssembler::Address(resultGPR, JSCell::classInfoOffset())); + m_jit.storePtr(MacroAssembler::TrustedImmPtr(&ClassType::s_info), MacroAssembler::Address(resultGPR, JSCell::classInfoOffset())); // Initialize the object's inheritorID. m_jit.storePtr(MacroAssembler::TrustedImmPtr(0), MacroAssembler::Address(resultGPR, JSObject::offsetOfInheritorID())); // Initialize the object's property storage pointer. m_jit.addPtr(MacroAssembler::TrustedImm32(sizeof(JSObject)), resultGPR, scratchGPR); - m_jit.storePtr(scratchGPR, MacroAssembler::Address(resultGPR, JSFinalObject::offsetOfPropertyStorage())); + m_jit.storePtr(scratchGPR, MacroAssembler::Address(resultGPR, ClassType::offsetOfPropertyStorage())); + } + + // It is acceptable to have structure be equal to scratch, so long as you're fine + // with the structure GPR being clobbered. + template<typename T> + void emitAllocateJSFinalObject(T structure, GPRReg resultGPR, GPRReg scratchGPR, MacroAssembler::JumpList& slowPath) + { + return emitAllocateBasicJSObject<JSFinalObject, false>(structure, resultGPR, scratchGPR, slowPath); } #if USE(JSVALUE64) @@ -1849,6 +1884,7 @@ private: // The current node being generated. BlockIndex m_block; NodeIndex m_compileIndex; + unsigned m_indexInBlock; // Virtual and physical register maps. Vector<GenerationInfo, 32> m_generationInfo; RegisterBank<GPRInfo> m_gprs; @@ -2482,6 +2518,7 @@ inline SpeculativeJIT::SpeculativeJIT(JITCompiler& jit) : m_compileOkay(true) , m_jit(jit) , m_compileIndex(0) + , m_indexInBlock(0) , m_generationInfo(m_jit.codeBlock()->m_numCalleeRegisters) , m_blockHeads(jit.graph().m_blocks.size()) , m_arguments(jit.codeBlock()->numParameters()) diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 8c4d8c030..b6814229c 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -652,14 +652,17 @@ void SpeculativeJIT::nonSpeculativePeepholeBranchNull(NodeUse operand, NodeIndex bool SpeculativeJIT::nonSpeculativeCompareNull(Node& node, NodeUse operand, bool invert) { - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + ASSERT(node.adjustedRefCount() == 1); nonSpeculativePeepholeBranchNull(operand, branchNodeIndex, invert); use(node.child1()); use(node.child2()); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; @@ -947,7 +950,7 @@ void SpeculativeJIT::emitCall(Node& node) m_jit.storePtr(resultPayloadGPR, callFramePayloadSlot(RegisterFile::ScopeChain)); m_jit.store32(MacroAssembler::TrustedImm32(JSValue::CellTag), callFrameTagSlot(RegisterFile::ScopeChain)); - m_jit.addPtr(Imm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); + m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); CodeOrigin codeOrigin = at(m_compileIndex).codeOrigin; CallBeginToken token = m_jit.beginJSCall(); @@ -958,12 +961,12 @@ void SpeculativeJIT::emitCall(Node& node) slowPath.link(&m_jit); - m_jit.addPtr(Imm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); m_jit.poke(GPRInfo::argumentGPR0); token = m_jit.beginCall(); JITCompiler::Call slowCall = m_jit.appendCall(slowCallFunction); m_jit.addFastExceptionCheck(slowCall, codeOrigin, token); - m_jit.addPtr(Imm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); + m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); token = m_jit.beginJSCall(); JITCompiler::Call theCall = m_jit.call(GPRInfo::returnValueGPR); m_jit.notifyCall(theCall, codeOrigin, token); @@ -1387,10 +1390,10 @@ void SpeculativeJIT::compileObjectEquality(Node& node, const ClassInfo* classInf GPRReg resultPayloadGPR = resultPayload.gpr(); MacroAssembler::Jump falseCase = m_jit.branchPtr(MacroAssembler::NotEqual, op1GPR, op2GPR); - m_jit.move(Imm32(1), resultPayloadGPR); + m_jit.move(TrustedImm32(1), resultPayloadGPR); MacroAssembler::Jump done = m_jit.jump(); falseCase.link(&m_jit); - m_jit.move(Imm32(0), resultPayloadGPR); + m_jit.move(TrustedImm32(0), resultPayloadGPR); done.link(&m_jit); booleanResult(resultPayloadGPR, m_compileIndex); @@ -1414,9 +1417,9 @@ void SpeculativeJIT::compileDoubleCompare(Node& node, MacroAssembler::DoubleCond SpeculateDoubleOperand op2(this, node.child2()); GPRTemporary resultPayload(this); - m_jit.move(Imm32(1), resultPayload.gpr()); + m_jit.move(TrustedImm32(1), resultPayload.gpr()); MacroAssembler::Jump trueCase = m_jit.branchDouble(condition, op1.fpr(), op2.fpr()); - m_jit.move(Imm32(0), resultPayload.gpr()); + m_jit.move(TrustedImm32(0), resultPayload.gpr()); trueCase.link(&m_jit); booleanResult(resultPayload.gpr(), m_compileIndex); @@ -1637,7 +1640,7 @@ void SpeculativeJIT::emitBranch(Node& node) void SpeculativeJIT::compile(Node& node) { - NodeType op = node.op; + NodeType op = static_cast<NodeType>(node.op); switch (op) { case JSConstant: @@ -1654,54 +1657,59 @@ void SpeculativeJIT::compile(Node& node) AbstractValue& value = block()->valuesAtHead.operand(node.local()); // If we have no prediction for this local, then don't attempt to compile. - if (prediction == PredictNone) { + if (prediction == PredictNone || value.isClear()) { terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); break; } - if (node.variableAccessData()->shouldUseDoubleFormat()) { - FPRTemporary result(this); - m_jit.loadDouble(JITCompiler::addressFor(node.local()), result.fpr()); - VirtualRegister virtualRegister = node.virtualRegister(); - m_fprs.retain(result.fpr(), virtualRegister, SpillOrderDouble); - m_generationInfo[virtualRegister].initDouble(m_compileIndex, node.refCount(), result.fpr()); - break; - } + if (!m_jit.graph().isCaptured(node.local())) { + if (node.variableAccessData()->shouldUseDoubleFormat()) { + FPRTemporary result(this); + m_jit.loadDouble(JITCompiler::addressFor(node.local()), result.fpr()); + VirtualRegister virtualRegister = node.virtualRegister(); + m_fprs.retain(result.fpr(), virtualRegister, SpillOrderDouble); + m_generationInfo[virtualRegister].initDouble(m_compileIndex, node.refCount(), result.fpr()); + break; + } - GPRTemporary result(this); - if (isInt32Prediction(prediction)) { - m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); - - // Like integerResult, but don't useChildren - our children are phi nodes, - // and don't represent values within this dataflow with virtual registers. - VirtualRegister virtualRegister = node.virtualRegister(); - m_gprs.retain(result.gpr(), virtualRegister, SpillOrderInteger); - m_generationInfo[virtualRegister].initInteger(m_compileIndex, node.refCount(), result.gpr()); - break; - } + if (isInt32Prediction(prediction)) { + GPRTemporary result(this); + m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); - if (isArrayPrediction(prediction) || isByteArrayPrediction(prediction)) { - m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); + // Like integerResult, but don't useChildren - our children are phi nodes, + // and don't represent values within this dataflow with virtual registers. + VirtualRegister virtualRegister = node.virtualRegister(); + m_gprs.retain(result.gpr(), virtualRegister, SpillOrderInteger); + m_generationInfo[virtualRegister].initInteger(m_compileIndex, node.refCount(), result.gpr()); + break; + } - // Like cellResult, but don't useChildren - our children are phi nodes, - // and don't represent values within this dataflow with virtual registers. - VirtualRegister virtualRegister = node.virtualRegister(); - m_gprs.retain(result.gpr(), virtualRegister, SpillOrderCell); - m_generationInfo[virtualRegister].initCell(m_compileIndex, node.refCount(), result.gpr()); - break; - } + if (isArrayPrediction(prediction) || isByteArrayPrediction(prediction)) { + GPRTemporary result(this); + m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); + + // Like cellResult, but don't useChildren - our children are phi nodes, + // and don't represent values within this dataflow with virtual registers. + VirtualRegister virtualRegister = node.virtualRegister(); + m_gprs.retain(result.gpr(), virtualRegister, SpillOrderCell); + m_generationInfo[virtualRegister].initCell(m_compileIndex, node.refCount(), result.gpr()); + break; + } - if (isBooleanPrediction(prediction)) { - m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); + if (isBooleanPrediction(prediction)) { + GPRTemporary result(this); + m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); - // Like booleanResult, but don't useChildren - our children are phi nodes, - // and don't represent values within this dataflow with virtual registers. - VirtualRegister virtualRegister = node.virtualRegister(); - m_gprs.retain(result.gpr(), virtualRegister, SpillOrderBoolean); - m_generationInfo[virtualRegister].initBoolean(m_compileIndex, node.refCount(), result.gpr()); - break; + // Like booleanResult, but don't useChildren - our children are phi nodes, + // and don't represent values within this dataflow with virtual registers. + VirtualRegister virtualRegister = node.virtualRegister(); + m_gprs.retain(result.gpr(), virtualRegister, SpillOrderBoolean); + m_generationInfo[virtualRegister].initBoolean(m_compileIndex, node.refCount(), result.gpr()); + break; + } } + GPRTemporary result(this); GPRTemporary tag(this); m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); m_jit.load32(JITCompiler::tagFor(node.local()), tag.gpr()); @@ -1712,7 +1720,12 @@ void SpeculativeJIT::compile(Node& node) m_gprs.retain(result.gpr(), virtualRegister, SpillOrderJS); m_gprs.retain(tag.gpr(), virtualRegister, SpillOrderJS); - DataFormat format = isCellPrediction(value.m_type) ? DataFormatJSCell : DataFormatJS; + DataFormat format; + if (isCellPrediction(value.m_type) + && !m_jit.graph().isCaptured(node.local())) + format = DataFormatJSCell; + else + format = DataFormatJS; m_generationInfo[virtualRegister].initJSValue(m_compileIndex, node.refCount(), tag.gpr(), result.gpr(), format); break; } @@ -1733,53 +1746,65 @@ void SpeculativeJIT::compile(Node& node) m_codeOriginForOSR = nextNode.codeOrigin; - if (node.variableAccessData()->shouldUseDoubleFormat()) { - SpeculateDoubleOperand value(this, node.child1()); - m_jit.storeDouble(value.fpr(), JITCompiler::addressFor(node.local())); - noResult(m_compileIndex); - // Indicate that it's no longer necessary to retrieve the value of - // this bytecode variable from registers or other locations in the register file, - // but that it is stored as a double. - valueSourceReferenceForOperand(node.local()) = ValueSource(DoubleInRegisterFile); - } else { + if (!m_jit.graph().isCaptured(node.local())) { + if (node.variableAccessData()->shouldUseDoubleFormat()) { + SpeculateDoubleOperand value(this, node.child1()); + m_jit.storeDouble(value.fpr(), JITCompiler::addressFor(node.local())); + noResult(m_compileIndex); + // Indicate that it's no longer necessary to retrieve the value of + // this bytecode variable from registers or other locations in the register file, + // but that it is stored as a double. + valueSourceReferenceForOperand(node.local()) = ValueSource(DoubleInRegisterFile); + break; + } PredictedType predictedType = node.variableAccessData()->prediction(); if (m_generationInfo[at(node.child1()).virtualRegister()].registerFormat() == DataFormatDouble) { DoubleOperand value(this, node.child1()); m_jit.storeDouble(value.fpr(), JITCompiler::addressFor(node.local())); noResult(m_compileIndex); - } else if (isInt32Prediction(predictedType)) { + valueSourceReferenceForOperand(node.local()) = ValueSource(DoubleInRegisterFile); + break; + } + if (isInt32Prediction(predictedType)) { SpeculateIntegerOperand value(this, node.child1()); m_jit.store32(value.gpr(), JITCompiler::payloadFor(node.local())); noResult(m_compileIndex); - } else if (isArrayPrediction(predictedType)) { + valueSourceReferenceForOperand(node.local()) = ValueSource(Int32InRegisterFile); + break; + } + if (isArrayPrediction(predictedType)) { SpeculateCellOperand cell(this, node.child1()); GPRReg cellGPR = cell.gpr(); if (!isArrayPrediction(m_state.forNode(node.child1()).m_type)) speculationCheck(BadType, JSValueSource::unboxedCell(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); m_jit.storePtr(cellGPR, JITCompiler::payloadFor(node.local())); noResult(m_compileIndex); - } else if (isByteArrayPrediction(predictedType)) { + valueSourceReferenceForOperand(node.local()) = ValueSource(CellInRegisterFile); + break; + } + if (isByteArrayPrediction(predictedType)) { SpeculateCellOperand cell(this, node.child1()); GPRReg cellGPR = cell.gpr(); if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type)) speculationCheck(BadType, JSValueSource::unboxedCell(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); m_jit.storePtr(cellGPR, JITCompiler::payloadFor(node.local())); noResult(m_compileIndex); - } else if (isBooleanPrediction(predictedType)) { + valueSourceReferenceForOperand(node.local()) = ValueSource(CellInRegisterFile); + break; + } + if (isBooleanPrediction(predictedType)) { SpeculateBooleanOperand value(this, node.child1()); m_jit.store32(value.gpr(), JITCompiler::payloadFor(node.local())); noResult(m_compileIndex); - } else { - JSValueOperand value(this, node.child1()); - m_jit.store32(value.payloadGPR(), JITCompiler::payloadFor(node.local())); - m_jit.store32(value.tagGPR(), JITCompiler::tagFor(node.local())); - noResult(m_compileIndex); + valueSourceReferenceForOperand(node.local()) = ValueSource(BooleanInRegisterFile); + break; } - - // Indicate that it's no longer necessary to retrieve the value of - // this bytecode variable from registers or other locations in the register file. - valueSourceReferenceForOperand(node.local()) = ValueSource::forPrediction(predictedType); } + JSValueOperand value(this, node.child1()); + m_jit.store32(value.payloadGPR(), JITCompiler::payloadFor(node.local())); + m_jit.store32(value.tagGPR(), JITCompiler::tagFor(node.local())); + noResult(m_compileIndex); + valueSourceReferenceForOperand(node.local()) = ValueSource(ValueInRegisterFile); break; } @@ -1863,6 +1888,10 @@ void SpeculativeJIT::compile(Node& node) compileArithSub(node); break; + case ArithNegate: + compileArithNegate(node); + break; + case ArithMul: compileArithMul(node); break; @@ -1951,7 +1980,7 @@ void SpeculativeJIT::compile(Node& node) case ArithAbs: { if (at(node.child1()).shouldSpeculateInteger() && node.canSpeculateInteger()) { SpeculateIntegerOperand op1(this, node.child1()); - GPRTemporary result(this); + GPRTemporary result(this, op1); GPRTemporary scratch(this); m_jit.zeroExtend32ToPtr(op1.gpr(), result.gpr()); @@ -2350,7 +2379,7 @@ void SpeculativeJIT::compile(Node& node) // Code to handle put beyond array bounds. silentSpillAllRegisters(scratchReg); - callOperation(operationPutByValBeyondArrayBounds, baseReg, propertyReg, valueTagReg, valuePayloadReg); + callOperation(m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, baseReg, propertyReg, valueTagReg, valuePayloadReg); silentFillAllRegisters(scratchReg); JITCompiler::Jump wasBeyondArrayBounds = m_jit.jump(); @@ -2514,10 +2543,10 @@ void SpeculativeJIT::compile(Node& node) m_jit.store32(valueTagGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); m_jit.store32(valuePayloadGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); - m_jit.add32(Imm32(1), storageLengthGPR); + m_jit.add32(TrustedImm32(1), storageLengthGPR); m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length))); - m_jit.add32(Imm32(1), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); - m_jit.move(Imm32(JSValue::Int32Tag), storageGPR); + m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); + m_jit.move(TrustedImm32(JSValue::Int32Tag), storageGPR); MacroAssembler::Jump done = m_jit.jump(); @@ -2554,7 +2583,7 @@ void SpeculativeJIT::compile(Node& node) MacroAssembler::Jump emptyArrayCase = m_jit.branchTest32(MacroAssembler::Zero, storageLengthGPR); - m_jit.sub32(Imm32(1), storageLengthGPR); + m_jit.sub32(TrustedImm32(1), storageLengthGPR); MacroAssembler::Jump slowCase = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(baseGPR, JSArray::vectorLengthOffset())); @@ -2563,11 +2592,11 @@ void SpeculativeJIT::compile(Node& node) m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length))); - MacroAssembler::Jump holeCase = m_jit.branch32(MacroAssembler::Equal, Imm32(JSValue::EmptyValueTag), valueTagGPR); + MacroAssembler::Jump holeCase = m_jit.branch32(MacroAssembler::Equal, TrustedImm32(JSValue::EmptyValueTag), valueTagGPR); m_jit.store32(TrustedImm32(JSValue::EmptyValueTag), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); - m_jit.sub32(MacroAssembler::Imm32(1), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); + m_jit.sub32(TrustedImm32(1), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); MacroAssembler::JumpList done; @@ -2575,8 +2604,8 @@ void SpeculativeJIT::compile(Node& node) holeCase.link(&m_jit); emptyArrayCase.link(&m_jit); - m_jit.move(MacroAssembler::Imm32(jsUndefined().tag()), valueTagGPR); - m_jit.move(MacroAssembler::Imm32(jsUndefined().payload()), valuePayloadGPR); + m_jit.move(MacroAssembler::TrustedImm32(jsUndefined().tag()), valueTagGPR); + m_jit.move(MacroAssembler::TrustedImm32(jsUndefined().payload()), valuePayloadGPR); done.append(m_jit.jump()); slowCase.link(&m_jit); @@ -3384,7 +3413,7 @@ void SpeculativeJIT::compile(Node& node) case Phi: case Flush: - ASSERT_NOT_REACHED(); + break; case Breakpoint: #if ENABLE(DEBUG_WITH_BREAKPOINT) @@ -3466,6 +3495,77 @@ void SpeculativeJIT::compile(Node& node) break; } + case CreateActivation: { + JSValueOperand value(this, node.child1()); + GPRTemporary result(this, value, false); + + GPRReg valueTagGPR = value.tagGPR(); + GPRReg valuePayloadGPR = value.payloadGPR(); + GPRReg resultGPR = result.gpr(); + + m_jit.move(valuePayloadGPR, resultGPR); + + JITCompiler::Jump alreadyCreated = m_jit.branch32(JITCompiler::NotEqual, valueTagGPR, TrustedImm32(JSValue::EmptyValueTag)); + + silentSpillAllRegisters(resultGPR); + callOperation(operationCreateActivation, resultGPR); + silentFillAllRegisters(resultGPR); + + alreadyCreated.link(&m_jit); + + cellResult(resultGPR, m_compileIndex); + break; + } + + case TearOffActivation: { + JSValueOperand value(this, node.child1()); + + GPRReg valueTagGPR = value.tagGPR(); + GPRReg valuePayloadGPR = value.payloadGPR(); + + JITCompiler::Jump notCreated = m_jit.branch32(JITCompiler::Equal, valueTagGPR, TrustedImm32(JSValue::EmptyValueTag)); + + silentSpillAllRegisters(InvalidGPRReg); + callOperation(operationTearOffActivation, valuePayloadGPR); + silentFillAllRegisters(InvalidGPRReg); + + notCreated.link(&m_jit); + + noResult(m_compileIndex); + break; + } + + case NewFunctionNoCheck: + compileNewFunctionNoCheck(node); + break; + + case NewFunction: { + JSValueOperand value(this, node.child1()); + GPRTemporary result(this, value, false); + + GPRReg valueTagGPR = value.tagGPR(); + GPRReg valuePayloadGPR = value.payloadGPR(); + GPRReg resultGPR = result.gpr(); + + m_jit.move(valuePayloadGPR, resultGPR); + + JITCompiler::Jump alreadyCreated = m_jit.branch32(JITCompiler::NotEqual, valueTagGPR, TrustedImm32(JSValue::EmptyValueTag)); + + silentSpillAllRegisters(resultGPR); + callOperation( + operationNewFunction, resultGPR, m_jit.codeBlock()->functionDecl(node.functionDeclIndex())); + silentFillAllRegisters(resultGPR); + + alreadyCreated.link(&m_jit); + + cellResult(resultGPR, m_compileIndex); + break; + } + + case NewFunctionExpression: + compileNewFunctionExpression(node); + break; + case ForceOSRExit: { terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); break; @@ -3478,10 +3578,11 @@ void SpeculativeJIT::compile(Node& node) case InlineStart: case Nop: + case LastNodeType: ASSERT_NOT_REACHED(); break; } - + if (!m_compileOkay) return; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index 6d375f81f..1597b1674 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -57,7 +57,7 @@ GPRReg SpeculativeJIT::fillInteger(NodeIndex nodeIndex, DataFormat& returnFormat } else { ASSERT(isJSConstant(nodeIndex)); JSValue jsValue = valueOfJSConstant(nodeIndex); - m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), gpr); + m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsValue)), gpr); } } else if (info.spillFormat() == DataFormatInteger) { m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); @@ -141,7 +141,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) // FIXME: should not be reachable? ASSERT(isJSConstant(nodeIndex)); JSValue jsValue = valueOfJSConstant(nodeIndex); - m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), gpr); + m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsValue)), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); info.fillJSValue(gpr, DataFormatJS); unlock(gpr); @@ -283,7 +283,7 @@ GPRReg SpeculativeJIT::fillJSValue(NodeIndex nodeIndex) } else { ASSERT(isJSConstant(nodeIndex)); JSValue jsValue = valueOfJSConstant(nodeIndex); - m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), gpr); + m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsValue)), gpr); info.fillJSValue(gpr, DataFormatJS); } @@ -628,14 +628,17 @@ void SpeculativeJIT::nonSpeculativePeepholeBranchNull(NodeUse operand, NodeIndex bool SpeculativeJIT::nonSpeculativeCompareNull(Node& node, NodeUse operand, bool invert) { - NodeIndex branchNodeIndex = detectPeepHoleBranch(); - if (branchNodeIndex != NoNode) { + unsigned branchIndexInBlock = detectPeepHoleBranch(); + if (branchIndexInBlock != UINT_MAX) { + NodeIndex branchNodeIndex = m_jit.graph().m_blocks[m_block]->at(branchIndexInBlock); + ASSERT(node.adjustedRefCount() == 1); nonSpeculativePeepholeBranchNull(operand, branchNodeIndex, invert); use(node.child1()); use(node.child2()); + m_indexInBlock = branchIndexInBlock; m_compileIndex = branchNodeIndex; return true; @@ -808,15 +811,21 @@ void SpeculativeJIT::nonSpeculativePeepholeStrictEq(Node& node, NodeIndex branch JITCompiler::Jump twoCellsCase = m_jit.branchTestPtr(JITCompiler::Zero, resultGPR, GPRInfo::tagMaskRegister); - JITCompiler::Jump numberCase = m_jit.branchTestPtr(JITCompiler::NonZero, resultGPR, GPRInfo::tagTypeNumberRegister); + JITCompiler::Jump leftOK = m_jit.branchPtr(JITCompiler::AboveOrEqual, arg1GPR, GPRInfo::tagTypeNumberRegister); + JITCompiler::Jump leftDouble = m_jit.branchTestPtr(JITCompiler::NonZero, arg1GPR, GPRInfo::tagTypeNumberRegister); + leftOK.link(&m_jit); + JITCompiler::Jump rightOK = m_jit.branchPtr(JITCompiler::AboveOrEqual, arg2GPR, GPRInfo::tagTypeNumberRegister); + JITCompiler::Jump rightDouble = m_jit.branchTestPtr(JITCompiler::NonZero, arg2GPR, GPRInfo::tagTypeNumberRegister); + rightOK.link(&m_jit); - branch32(invert ? JITCompiler::NotEqual : JITCompiler::Equal, arg1GPR, arg2GPR, taken); + branchPtr(invert ? JITCompiler::NotEqual : JITCompiler::Equal, arg1GPR, arg2GPR, taken); jump(notTaken, ForceJump); twoCellsCase.link(&m_jit); branchPtr(JITCompiler::Equal, arg1GPR, arg2GPR, invert ? notTaken : taken); - numberCase.link(&m_jit); + leftDouble.link(&m_jit); + rightDouble.link(&m_jit); silentSpillAllRegisters(resultGPR); callOperation(operationCompareStrictEq, resultGPR, arg1GPR, arg2GPR); @@ -865,9 +874,14 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeStrictEq(Node& node, bool invert) JITCompiler::Jump twoCellsCase = m_jit.branchTestPtr(JITCompiler::Zero, resultGPR, GPRInfo::tagMaskRegister); - JITCompiler::Jump numberCase = m_jit.branchTestPtr(JITCompiler::NonZero, resultGPR, GPRInfo::tagTypeNumberRegister); + JITCompiler::Jump leftOK = m_jit.branchPtr(JITCompiler::AboveOrEqual, arg1GPR, GPRInfo::tagTypeNumberRegister); + JITCompiler::Jump leftDouble = m_jit.branchTestPtr(JITCompiler::NonZero, arg1GPR, GPRInfo::tagTypeNumberRegister); + leftOK.link(&m_jit); + JITCompiler::Jump rightOK = m_jit.branchPtr(JITCompiler::AboveOrEqual, arg2GPR, GPRInfo::tagTypeNumberRegister); + JITCompiler::Jump rightDouble = m_jit.branchTestPtr(JITCompiler::NonZero, arg2GPR, GPRInfo::tagTypeNumberRegister); + rightOK.link(&m_jit); - m_jit.compare32(invert ? JITCompiler::NotEqual : JITCompiler::Equal, arg1GPR, arg2GPR, resultGPR); + m_jit.comparePtr(invert ? JITCompiler::NotEqual : JITCompiler::Equal, arg1GPR, arg2GPR, resultGPR); JITCompiler::Jump done1 = m_jit.jump(); @@ -878,7 +892,8 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeStrictEq(Node& node, bool invert) JITCompiler::Jump done2 = m_jit.jump(); - numberCase.link(&m_jit); + leftDouble.link(&m_jit); + rightDouble.link(&m_jit); notEqualCase.link(&m_jit); silentSpillAllRegisters(resultGPR); @@ -948,7 +963,7 @@ void SpeculativeJIT::emitCall(Node& node) m_jit.loadPtr(MacroAssembler::Address(calleeGPR, OBJECT_OFFSETOF(JSFunction, m_scopeChain)), resultGPR); m_jit.storePtr(resultGPR, callFrameSlot(RegisterFile::ScopeChain)); - m_jit.addPtr(Imm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); + m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); CodeOrigin codeOrigin = at(m_compileIndex).codeOrigin; CallBeginToken token = m_jit.beginJSCall(); @@ -959,11 +974,11 @@ void SpeculativeJIT::emitCall(Node& node) slowPath.link(&m_jit); - m_jit.addPtr(Imm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); token = m_jit.beginCall(); JITCompiler::Call slowCall = m_jit.appendCall(slowCallFunction); m_jit.addFastExceptionCheck(slowCall, codeOrigin, token); - m_jit.addPtr(Imm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); + m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); token = m_jit.beginJSCall(); JITCompiler::Call theCall = m_jit.call(GPRInfo::returnValueGPR); m_jit.notifyCall(theCall, codeOrigin, token); @@ -1461,10 +1476,10 @@ void SpeculativeJIT::compileObjectEquality(Node& node, const ClassInfo* classInf speculationCheck(BadType, JSValueRegs(op2GPR), node.child2().index(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op2GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(classInfo))); MacroAssembler::Jump falseCase = m_jit.branchPtr(MacroAssembler::NotEqual, op1GPR, op2GPR); - m_jit.move(Imm32(ValueTrue), resultGPR); + m_jit.move(TrustedImm32(ValueTrue), resultGPR); MacroAssembler::Jump done = m_jit.jump(); falseCase.link(&m_jit); - m_jit.move(Imm32(ValueFalse), resultGPR); + m_jit.move(TrustedImm32(ValueFalse), resultGPR); done.link(&m_jit); jsValueResult(resultGPR, m_compileIndex, DataFormatJSBoolean); @@ -1491,7 +1506,7 @@ void SpeculativeJIT::compileDoubleCompare(Node& node, MacroAssembler::DoubleCond m_jit.move(TrustedImm32(ValueTrue), result.gpr()); MacroAssembler::Jump trueCase = m_jit.branchDouble(condition, op1.fpr(), op2.fpr()); - m_jit.xorPtr(Imm32(true), result.gpr()); + m_jit.xorPtr(TrustedImm32(true), result.gpr()); trueCase.link(&m_jit); jsValueResult(result.gpr(), m_compileIndex, DataFormatJSBoolean); @@ -1577,7 +1592,7 @@ void SpeculativeJIT::compileLogicalNot(Node& node) GPRTemporary result(this); m_jit.move(TrustedImm32(ValueFalse), result.gpr()); MacroAssembler::Jump nonZero = m_jit.branchDoubleNonZero(value.fpr(), scratch.fpr()); - m_jit.xor32(Imm32(true), result.gpr()); + m_jit.xor32(TrustedImm32(true), result.gpr()); nonZero.link(&m_jit); jsValueResult(result.gpr(), m_compileIndex, DataFormatJSBoolean); return; @@ -1699,18 +1714,18 @@ void SpeculativeJIT::emitBranch(Node& node) bool predictBoolean = isBooleanPrediction(m_jit.getPrediction(node.child1())); if (predictBoolean) { - branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::ImmPtr(JSValue::encode(jsBoolean(false))), notTaken); - branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::ImmPtr(JSValue::encode(jsBoolean(true))), taken); + branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(false))), notTaken); + branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(true))), taken); speculationCheck(BadType, JSValueRegs(valueGPR), node.child1(), m_jit.jump()); value.use(); } else { - branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::ImmPtr(JSValue::encode(jsNumber(0))), notTaken); + branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::TrustedImmPtr(JSValue::encode(jsNumber(0))), notTaken); branchPtr(MacroAssembler::AboveOrEqual, valueGPR, GPRInfo::tagTypeNumberRegister, taken); if (!predictBoolean) { - branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::ImmPtr(JSValue::encode(jsBoolean(false))), notTaken); - branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::ImmPtr(JSValue::encode(jsBoolean(true))), taken); + branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(false))), notTaken); + branchPtr(MacroAssembler::Equal, valueGPR, MacroAssembler::TrustedImmPtr(JSValue::encode(jsBoolean(true))), taken); } value.use(); @@ -1729,7 +1744,7 @@ void SpeculativeJIT::emitBranch(Node& node) void SpeculativeJIT::compile(Node& node) { - NodeType op = node.op; + NodeType op = static_cast<NodeType>(node.op); switch (op) { case JSConstant: @@ -1751,27 +1766,30 @@ void SpeculativeJIT::compile(Node& node) break; } - if (node.variableAccessData()->shouldUseDoubleFormat()) { - FPRTemporary result(this); - m_jit.loadDouble(JITCompiler::addressFor(node.local()), result.fpr()); - VirtualRegister virtualRegister = node.virtualRegister(); - m_fprs.retain(result.fpr(), virtualRegister, SpillOrderDouble); - m_generationInfo[virtualRegister].initDouble(m_compileIndex, node.refCount(), result.fpr()); - break; - } - - GPRTemporary result(this); - if (isInt32Prediction(value.m_type)) { - m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); - - // Like integerResult, but don't useChildren - our children are phi nodes, - // and don't represent values within this dataflow with virtual registers. - VirtualRegister virtualRegister = node.virtualRegister(); - m_gprs.retain(result.gpr(), virtualRegister, SpillOrderInteger); - m_generationInfo[virtualRegister].initInteger(m_compileIndex, node.refCount(), result.gpr()); - break; + if (!m_jit.graph().isCaptured(node.local())) { + if (node.variableAccessData()->shouldUseDoubleFormat()) { + FPRTemporary result(this); + m_jit.loadDouble(JITCompiler::addressFor(node.local()), result.fpr()); + VirtualRegister virtualRegister = node.virtualRegister(); + m_fprs.retain(result.fpr(), virtualRegister, SpillOrderDouble); + m_generationInfo[virtualRegister].initDouble(m_compileIndex, node.refCount(), result.fpr()); + break; + } + + if (isInt32Prediction(value.m_type)) { + GPRTemporary result(this); + m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr()); + + // Like integerResult, but don't useChildren - our children are phi nodes, + // and don't represent values within this dataflow with virtual registers. + VirtualRegister virtualRegister = node.virtualRegister(); + m_gprs.retain(result.gpr(), virtualRegister, SpillOrderInteger); + m_generationInfo[virtualRegister].initInteger(m_compileIndex, node.refCount(), result.gpr()); + break; + } } + GPRTemporary result(this); m_jit.loadPtr(JITCompiler::addressFor(node.local()), result.gpr()); // Like jsValueResult, but don't useChildren - our children are phi nodes, @@ -1780,7 +1798,9 @@ void SpeculativeJIT::compile(Node& node) m_gprs.retain(result.gpr(), virtualRegister, SpillOrderJS); DataFormat format; - if (isCellPrediction(value.m_type)) + if (m_jit.graph().isCaptured(node.local())) + format = DataFormatJS; + else if (isCellPrediction(value.m_type)) format = DataFormatJSCell; else if (isBooleanPrediction(value.m_type)) format = DataFormatJSBoolean; @@ -1817,48 +1837,60 @@ void SpeculativeJIT::compile(Node& node) // OSR exit, would not be visible to the old JIT in any way. m_codeOriginForOSR = nextNode.codeOrigin; - if (node.variableAccessData()->shouldUseDoubleFormat()) { - SpeculateDoubleOperand value(this, node.child1()); - m_jit.storeDouble(value.fpr(), JITCompiler::addressFor(node.local())); - noResult(m_compileIndex); - // Indicate that it's no longer necessary to retrieve the value of - // this bytecode variable from registers or other locations in the register file, - // but that it is stored as a double. - valueSourceReferenceForOperand(node.local()) = ValueSource(DoubleInRegisterFile); - } else { + if (!m_jit.graph().isCaptured(node.local())) { + if (node.variableAccessData()->shouldUseDoubleFormat()) { + SpeculateDoubleOperand value(this, node.child1()); + m_jit.storeDouble(value.fpr(), JITCompiler::addressFor(node.local())); + noResult(m_compileIndex); + // Indicate that it's no longer necessary to retrieve the value of + // this bytecode variable from registers or other locations in the register file, + // but that it is stored as a double. + valueSourceReferenceForOperand(node.local()) = ValueSource(DoubleInRegisterFile); + break; + } + PredictedType predictedType = node.variableAccessData()->prediction(); if (isInt32Prediction(predictedType)) { SpeculateIntegerOperand value(this, node.child1()); m_jit.store32(value.gpr(), JITCompiler::payloadFor(node.local())); noResult(m_compileIndex); - } else if (isArrayPrediction(predictedType)) { + valueSourceReferenceForOperand(node.local()) = ValueSource(Int32InRegisterFile); + break; + } + if (isArrayPrediction(predictedType)) { SpeculateCellOperand cell(this, node.child1()); GPRReg cellGPR = cell.gpr(); if (!isArrayPrediction(m_state.forNode(node.child1()).m_type)) speculationCheck(BadType, JSValueRegs(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); m_jit.storePtr(cellGPR, JITCompiler::addressFor(node.local())); noResult(m_compileIndex); - } else if (isByteArrayPrediction(predictedType)) { + valueSourceReferenceForOperand(node.local()) = ValueSource(CellInRegisterFile); + break; + } + if (isByteArrayPrediction(predictedType)) { SpeculateCellOperand cell(this, node.child1()); GPRReg cellGPR = cell.gpr(); if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type)) speculationCheck(BadType, JSValueRegs(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSByteArray::s_info))); m_jit.storePtr(cellGPR, JITCompiler::addressFor(node.local())); noResult(m_compileIndex); - } else if (isBooleanPrediction(predictedType)) { + valueSourceReferenceForOperand(node.local()) = ValueSource(CellInRegisterFile); + break; + } + if (isBooleanPrediction(predictedType)) { SpeculateBooleanOperand boolean(this, node.child1()); m_jit.storePtr(boolean.gpr(), JITCompiler::addressFor(node.local())); noResult(m_compileIndex); - } else { - JSValueOperand value(this, node.child1()); - m_jit.storePtr(value.gpr(), JITCompiler::addressFor(node.local())); - noResult(m_compileIndex); + valueSourceReferenceForOperand(node.local()) = ValueSource(BooleanInRegisterFile); + break; } - - // Indicate that it's no longer necessary to retrieve the value of - // this bytecode variable from registers or other locations in the register file. - valueSourceReferenceForOperand(node.local()) = ValueSource::forPrediction(predictedType); } + + JSValueOperand value(this, node.child1()); + m_jit.storePtr(value.gpr(), JITCompiler::addressFor(node.local())); + noResult(m_compileIndex); + + valueSourceReferenceForOperand(node.local()) = ValueSource(ValueInRegisterFile); break; } @@ -1942,6 +1974,10 @@ void SpeculativeJIT::compile(Node& node) compileArithSub(node); break; + case ArithNegate: + compileArithNegate(node); + break; + case ArithMul: compileArithMul(node); break; @@ -2007,7 +2043,7 @@ void SpeculativeJIT::compile(Node& node) case ArithAbs: { if (at(node.child1()).shouldSpeculateInteger() && node.canSpeculateInteger()) { SpeculateIntegerOperand op1(this, node.child1()); - GPRTemporary result(this, op1); + GPRTemporary result(this); GPRTemporary scratch(this); m_jit.zeroExtend32ToPtr(op1.gpr(), result.gpr()); @@ -2389,7 +2425,7 @@ void SpeculativeJIT::compile(Node& node) // Code to handle put beyond array bounds. silentSpillAllRegisters(scratchReg); - callOperation(operationPutByValBeyondArrayBounds, baseReg, propertyReg, valueReg); + callOperation(m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, baseReg, propertyReg, valueReg); silentFillAllRegisters(scratchReg); JITCompiler::Jump wasBeyondArrayBounds = m_jit.jump(); @@ -2549,9 +2585,9 @@ void SpeculativeJIT::compile(Node& node) m_jit.storePtr(valueGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); - m_jit.add32(Imm32(1), storageLengthGPR); + m_jit.add32(TrustedImm32(1), storageLengthGPR); m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length))); - m_jit.add32(Imm32(1), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); + m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); m_jit.orPtr(GPRInfo::tagTypeNumberRegister, storageLengthGPR); MacroAssembler::Jump done = m_jit.jump(); @@ -2587,7 +2623,7 @@ void SpeculativeJIT::compile(Node& node) MacroAssembler::Jump emptyArrayCase = m_jit.branchTest32(MacroAssembler::Zero, storageLengthGPR); - m_jit.sub32(Imm32(1), storageLengthGPR); + m_jit.sub32(TrustedImm32(1), storageLengthGPR); MacroAssembler::Jump slowCase = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(baseGPR, JSArray::vectorLengthOffset())); @@ -2598,7 +2634,7 @@ void SpeculativeJIT::compile(Node& node) MacroAssembler::Jump holeCase = m_jit.branchTestPtr(MacroAssembler::Zero, valueGPR); m_jit.storePtr(MacroAssembler::TrustedImmPtr(0), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); - m_jit.sub32(MacroAssembler::Imm32(1), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); + m_jit.sub32(MacroAssembler::TrustedImm32(1), MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector))); MacroAssembler::JumpList done; @@ -3358,9 +3394,9 @@ void SpeculativeJIT::compile(Node& node) break; } - case Phi: case Flush: - ASSERT_NOT_REACHED(); + case Phi: + break; case Breakpoint: #if ENABLE(DEBUG_WITH_BREAKPOINT) @@ -3434,6 +3470,73 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultGPR, m_compileIndex); break; } + + case CreateActivation: { + JSValueOperand value(this, node.child1()); + GPRTemporary result(this, value); + + GPRReg valueGPR = value.gpr(); + GPRReg resultGPR = result.gpr(); + + m_jit.move(valueGPR, resultGPR); + + JITCompiler::Jump alreadyCreated = m_jit.branchTestPtr(JITCompiler::NonZero, resultGPR); + + silentSpillAllRegisters(resultGPR); + callOperation(operationCreateActivation, resultGPR); + silentFillAllRegisters(resultGPR); + + alreadyCreated.link(&m_jit); + + cellResult(resultGPR, m_compileIndex); + break; + } + + case TearOffActivation: { + JSValueOperand value(this, node.child1()); + GPRReg valueGPR = value.gpr(); + + JITCompiler::Jump notCreated = m_jit.branchTestPtr(JITCompiler::Zero, valueGPR); + + silentSpillAllRegisters(InvalidGPRReg); + callOperation(operationTearOffActivation, valueGPR); + silentFillAllRegisters(InvalidGPRReg); + + notCreated.link(&m_jit); + + noResult(m_compileIndex); + break; + } + + case NewFunctionNoCheck: + compileNewFunctionNoCheck(node); + break; + + case NewFunction: { + JSValueOperand value(this, node.child1()); + GPRTemporary result(this, value); + + GPRReg valueGPR = value.gpr(); + GPRReg resultGPR = result.gpr(); + + m_jit.move(valueGPR, resultGPR); + + JITCompiler::Jump alreadyCreated = m_jit.branchTestPtr(JITCompiler::NonZero, resultGPR); + + silentSpillAllRegisters(resultGPR); + callOperation( + operationNewFunction, resultGPR, m_jit.codeBlock()->functionDecl(node.functionDeclIndex())); + silentFillAllRegisters(resultGPR); + + alreadyCreated.link(&m_jit); + + cellResult(resultGPR, m_compileIndex); + break; + } + + case NewFunctionExpression: + compileNewFunctionExpression(node); + break; case ForceOSRExit: { terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); @@ -3449,6 +3552,10 @@ void SpeculativeJIT::compile(Node& node) case Nop: ASSERT_NOT_REACHED(); break; + + case LastNodeType: + ASSERT_NOT_REACHED(); + break; } if (!m_compileOkay) diff --git a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp index c0b9fae65..255003612 100644 --- a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp @@ -48,39 +48,62 @@ public: dataLog("\n"); #endif ScoreBoard scoreBoard(m_graph, m_graph.m_preservedVars); - unsigned sizeExcludingPhiNodes = m_graph.m_blocks.last()->end; - for (size_t i = 0; i < sizeExcludingPhiNodes; ++i) { - Node& node = m_graph[i]; - - if (!node.shouldGenerate()) - continue; + scoreBoard.assertClear(); +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + bool needsNewLine = false; +#endif + for (size_t blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { + BasicBlock* block = m_graph.m_blocks[blockIndex].get(); + for (size_t indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { + NodeIndex nodeIndex = block->at(indexInBlock); +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + if (needsNewLine) + dataLog("\n"); + dataLog(" @%u:", nodeIndex); + needsNewLine = true; +#endif + Node& node = m_graph[nodeIndex]; - // GetLocal nodes are effectively phi nodes in the graph, referencing - // results from prior blocks. - if (node.op != GetLocal) { - // First, call use on all of the current node's children, then - // allocate a VirtualRegister for this node. We do so in this - // order so that if a child is on its last use, and a - // VirtualRegister is freed, then it may be reused for node. - if (node.op & NodeHasVarArgs) { - for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) - scoreBoard.use(m_graph.m_varArgChildren[childIdx]); - } else { - scoreBoard.use(node.child1()); - scoreBoard.use(node.child2()); - scoreBoard.use(node.child3()); + if (!node.shouldGenerate() || node.op == Phi || node.op == Flush) + continue; + + // GetLocal nodes are effectively phi nodes in the graph, referencing + // results from prior blocks. + if (node.op != GetLocal) { + // First, call use on all of the current node's children, then + // allocate a VirtualRegister for this node. We do so in this + // order so that if a child is on its last use, and a + // VirtualRegister is freed, then it may be reused for node. + if (node.flags & NodeHasVarArgs) { + for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) + scoreBoard.use(m_graph.m_varArgChildren[childIdx]); + } else { + scoreBoard.use(node.child1()); + scoreBoard.use(node.child2()); + scoreBoard.use(node.child3()); + } } - } - if (!node.hasResult()) - continue; + if (!node.hasResult()) + continue; - node.setVirtualRegister(scoreBoard.allocate()); - // 'mustGenerate' nodes have their useCount artificially elevated, - // call use now to account for this. - if (node.mustGenerate()) - scoreBoard.use(i); + VirtualRegister virtualRegister = scoreBoard.allocate(); +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + dataLog(" Assigning virtual register %u to node %u.", + virtualRegister, nodeIndex); +#endif + node.setVirtualRegister(virtualRegister); + // 'mustGenerate' nodes have their useCount artificially elevated, + // call use now to account for this. + if (node.mustGenerate()) + scoreBoard.use(nodeIndex); + } + scoreBoard.assertClear(); } +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + if (needsNewLine) + dataLog("\n"); +#endif // 'm_numCalleeRegisters' is the number of locals and temporaries allocated // for the function (and checked for on entry). Since we perform a new and |