diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-06-01 10:36:58 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-06-01 10:36:58 +0200 |
commit | b1e9e47fa11f608ae16bc07f97a2acf95bf80272 (patch) | |
tree | c88c45e80c9c44506e7cdf9a3bb39ebf82a8cd5b /Source/JavaScriptCore/dfg | |
parent | be01689f43cf6882cf670d33df49ead1f570c53a (diff) | |
download | qtwebkit-b1e9e47fa11f608ae16bc07f97a2acf95bf80272.tar.gz |
Imported WebKit commit 499c84c99aa98e9870fa7eaa57db476c6d160d46 (http://svn.webkit.org/repository/webkit/trunk@119200)
Weekly update :). Particularly relevant changes for Qt are the use of the WebCore image decoders and direct usage
of libpng/libjpeg if available in the system.
Diffstat (limited to 'Source/JavaScriptCore/dfg')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGAbstractState.cpp | 194 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGAbstractState.h | 8 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp | 152 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGCSEPhase.cpp | 49 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp | 37 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGJITCompiler.h | 4 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGOperations.cpp | 25 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 20 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h | 15 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp | 48 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp | 48 |
11 files changed, 339 insertions, 261 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index ff737cf1d..a0849acea 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -33,21 +33,6 @@ namespace JSC { namespace DFG { -#define CFA_PROFILING 0 - -#if CFA_PROFILING -#define PROFILE(flag) SamplingFlags::ScopedFlag scopedFlag(flag) -#else -#define PROFILE(flag) do { } while (false) -#endif - -// Profiling flags -#define FLAG_FOR_BLOCK_INITIALIZATION 17 -#define FLAG_FOR_BLOCK_END 18 -#define FLAG_FOR_EXECUTION 19 -#define FLAG_FOR_MERGE_TO_SUCCESSORS 20 -#define FLAG_FOR_STRUCTURE_CLOBBERING 21 - AbstractState::AbstractState(Graph& graph) : m_codeBlock(graph.m_codeBlock) , m_graph(graph) @@ -61,8 +46,6 @@ AbstractState::~AbstractState() { } void AbstractState::beginBasicBlock(BasicBlock* basicBlock) { - PROFILE(FLAG_FOR_BLOCK_INITIALIZATION); - ASSERT(!m_block); ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->valuesAtHead.numberOfLocals()); @@ -97,7 +80,6 @@ void AbstractState::beginBasicBlock(BasicBlock* basicBlock) void AbstractState::initialize(Graph& graph) { - PROFILE(FLAG_FOR_BLOCK_INITIALIZATION); BasicBlock* root = graph.m_blocks[0].get(); root->cfaShouldRevisit = true; root->cfaHasVisited = false; @@ -177,7 +159,6 @@ void AbstractState::initialize(Graph& graph) bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDirectionPtr) { - PROFILE(FLAG_FOR_BLOCK_END); ASSERT(m_block); BasicBlock* block = m_block; // Save the block for successor merging. @@ -197,14 +178,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi dataLog(" Merging state for argument %zu.\n", argument); #endif AbstractValue& destination = block->valuesAtTail.argument(argument); - NodeIndex nodeIndex = block->variablesAtTail.argument(argument); - if (nodeIndex != NoNode && m_graph[nodeIndex].variableAccessData()->isCaptured()) { - if (!destination.isTop()) { - destination.makeTop(); - changed = true; - } - } else - changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument)); + changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument)); } for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) { @@ -212,14 +186,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi dataLog(" Merging state for local %zu.\n", local); #endif AbstractValue& destination = block->valuesAtTail.local(local); - NodeIndex nodeIndex = block->variablesAtTail.local(local); - if (nodeIndex != NoNode && m_graph[nodeIndex].variableAccessData()->isCaptured()) { - if (!destination.isTop()) { - destination.makeTop(); - changed = true; - } - } else - changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local)); + changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local)); } } @@ -250,7 +217,6 @@ void AbstractState::reset() bool AbstractState::execute(unsigned indexInBlock) { - PROFILE(FLAG_FOR_EXECUTION); ASSERT(m_block); ASSERT(m_isValid); @@ -274,11 +240,13 @@ bool AbstractState::execute(unsigned indexInBlock) bool canExit = false; canExit |= variableAccessData->prediction() == PredictNone; if (variableAccessData->isCaptured()) - forNode(nodeIndex).makeTop(); + forNode(nodeIndex) = m_variables.operand(variableAccessData->local()); else { AbstractValue value = m_variables.operand(variableAccessData->local()); if (value.isClear()) canExit |= true; + if (value.value()) + m_foundConstants = true; forNode(nodeIndex) = value; } node.setCanExit(canExit); @@ -286,13 +254,14 @@ bool AbstractState::execute(unsigned indexInBlock) } case GetLocalUnlinked: { - forNode(nodeIndex).makeTop(); + forNode(nodeIndex) = m_variables.operand(node.unlinkedLocal()); node.setCanExit(false); break; } case SetLocal: { if (node.variableAccessData()->isCaptured()) { + m_variables.operand(node.local()) = forNode(node.child1()); node.setCanExit(false); break; } @@ -466,7 +435,7 @@ bool AbstractState::execute(unsigned indexInBlock) break; } if (node.op() == ValueAdd) { - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).set(PredictString | PredictInt32 | PredictNumber); node.setCanExit(false); break; @@ -772,12 +741,12 @@ bool AbstractState::execute(unsigned indexInBlock) } else { filter = PredictTop; checker = isAnyPrediction; - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); } } else { filter = PredictTop; checker = isAnyPrediction; - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); } node.setCanExit( !checker(forNode(node.child1()).m_type) @@ -864,7 +833,7 @@ bool AbstractState::execute(unsigned indexInBlock) break; } if (!isActionableArrayPrediction(m_graph[node.child1()].prediction()) || !m_graph[node.child2()].shouldSpeculateInteger()) { - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; } @@ -958,7 +927,7 @@ bool AbstractState::execute(unsigned indexInBlock) #endif ) { ASSERT(node.op() == PutByVal); - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; } @@ -1241,7 +1210,10 @@ bool AbstractState::execute(unsigned indexInBlock) break; case CheckArgumentsNotCreated: - node.setCanExit(true); + node.setCanExit( + !isEmptyPrediction( + m_variables.operand( + m_graph.argumentsRegisterFor(node.codeOrigin)).m_type)); break; case GetMyArgumentsLength: @@ -1249,20 +1221,21 @@ bool AbstractState::execute(unsigned indexInBlock) // the arguments a bit. Note that this is not sufficient to force constant folding // of GetMyArgumentsLength, because GetMyArgumentsLength is a clobbering operation. // We perform further optimizations on this later on. - if (node.codeOrigin.inlineCallFrame) { + if (node.codeOrigin.inlineCallFrame) forNode(nodeIndex).set(jsNumber(node.codeOrigin.inlineCallFrame->arguments.size() - 1)); - node.setCanExit(false); - break; - } - node.setCanExit(true); - forNode(nodeIndex).set(PredictInt32); + else + forNode(nodeIndex).set(PredictInt32); + node.setCanExit( + !isEmptyPrediction( + m_variables.operand( + m_graph.argumentsRegisterFor(node.codeOrigin)).m_type)); break; case GetMyArgumentsLengthSafe: node.setCanExit(false); // This potentially clobbers all structures if the arguments object had a getter // installed on the length property. - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); // We currently make no guarantee about what this returns because it does not // speculate that the length property is actually a length. forNode(nodeIndex).makeTop(); @@ -1278,10 +1251,10 @@ bool AbstractState::execute(unsigned indexInBlock) break; case GetMyArgumentByValSafe: - node.setCanExit(false); + node.setCanExit(true); // This potentially clobbers all structures if the property we're accessing has // a getter. We don't speculate against this. - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); // But we do speculate that the index is an integer. forNode(node.child1()).filter(PredictInt32); // And the result is unknown. @@ -1324,7 +1297,7 @@ bool AbstractState::execute(unsigned indexInBlock) } if (isCellPrediction(m_graph[node.child1()].prediction())) forNode(node.child1()).filter(PredictCell); - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; @@ -1502,7 +1475,7 @@ bool AbstractState::execute(unsigned indexInBlock) case PutByIdDirect: node.setCanExit(true); forNode(node.child1()).filter(PredictCell); - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); break; case GetGlobalVar: @@ -1545,7 +1518,7 @@ bool AbstractState::execute(unsigned indexInBlock) case ResolveBaseStrictPut: case ResolveGlobal: node.setCanExit(true); - clobberStructures(indexInBlock); + clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; @@ -1568,16 +1541,35 @@ bool AbstractState::execute(unsigned indexInBlock) return m_isValid; } +inline void AbstractState::clobberWorld(const CodeOrigin& codeOrigin, unsigned indexInBlock) +{ + if (codeOrigin.inlineCallFrame) { + const BitVector& capturedVars = codeOrigin.inlineCallFrame->capturedVars; + for (size_t i = capturedVars.size(); i--;) { + if (!capturedVars.quickGet(i)) + continue; + m_variables.local(i).makeTop(); + } + } else { + for (size_t i = m_codeBlock->m_numCapturedVars; i--;) + m_variables.local(i).makeTop(); + } + if (m_codeBlock->argumentsAreCaptured()) { + for (size_t i = m_variables.numberOfArguments(); i--;) + m_variables.argument(i).makeTop(); + } + clobberStructures(indexInBlock); +} + inline void AbstractState::clobberStructures(unsigned indexInBlock) { - PROFILE(FLAG_FOR_STRUCTURE_CLOBBERING); if (!m_haveStructures) return; for (size_t i = indexInBlock + 1; i--;) forNode(m_block->at(i)).clobberStructures(); - for (size_t i = 0; i < m_variables.numberOfArguments(); ++i) + for (size_t i = m_variables.numberOfArguments(); i--;) m_variables.argument(i).clobberStructures(); - for (size_t i = 0; i < m_variables.numberOfLocals(); ++i) + for (size_t i = m_variables.numberOfLocals(); i--;) m_variables.local(i).clobberStructures(); m_haveStructures = false; } @@ -1597,46 +1589,56 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract dataLog(" It's live, node @%u.\n", nodeIndex); #endif - switch (node.op()) { - case Phi: - case SetArgument: - case Flush: - // The block transfers the value from head to tail. + if (node.variableAccessData()->isCaptured()) { source = inVariable; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" Transfering "); source.dump(WTF::dataFile()); - dataLog(" from head to tail.\n"); + dataLog(" from last access due to captured variable.\n"); #endif - break; + } else { + switch (node.op()) { + case Phi: + case SetArgument: + case Flush: + // The block transfers the value from head to tail. + source = inVariable; +#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) + dataLog(" Transfering "); + source.dump(WTF::dataFile()); + dataLog(" from head to tail.\n"); +#endif + break; - case GetLocal: - // The block refines the value with additional speculations. - source = forNode(nodeIndex); + case GetLocal: + // The block refines the value with additional speculations. + source = forNode(nodeIndex); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Refining to "); - source.dump(WTF::dataFile()); - dataLog("\n"); + dataLog(" Refining to "); + source.dump(WTF::dataFile()); + dataLog("\n"); #endif - break; + break; - case SetLocal: - // The block sets the variable, and potentially refines it, both - // before and after setting it. - if (node.variableAccessData()->shouldUseDoubleFormat()) - source.set(PredictDouble); - else - source = forNode(node.child1()); + case SetLocal: + // The block sets the variable, and potentially refines it, both + // before and after setting it. + if (node.variableAccessData()->shouldUseDoubleFormat()) { + // FIXME: This unnecessarily loses precision. + source.set(PredictDouble); + } else + source = forNode(node.child1()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Setting to "); - source.dump(WTF::dataFile()); - dataLog("\n"); + dataLog(" Setting to "); + source.dump(WTF::dataFile()); + dataLog("\n"); #endif - break; + break; - default: - ASSERT_NOT_REACHED(); - break; + default: + ASSERT_NOT_REACHED(); + break; + } } if (destination == source) { @@ -1667,27 +1669,11 @@ inline bool AbstractState::merge(BasicBlock* from, BasicBlock* to) for (size_t argument = 0; argument < from->variablesAtTail.numberOfArguments(); ++argument) { AbstractValue& destination = to->valuesAtHead.argument(argument); - NodeIndex nodeIndex = from->variablesAtTail.argument(argument); - if (nodeIndex != NoNode && m_graph[nodeIndex].variableAccessData()->isCaptured()) { - 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) { AbstractValue& destination = to->valuesAtHead.local(local); - NodeIndex nodeIndex = from->variablesAtTail.local(local); - if (nodeIndex != NoNode && m_graph[nodeIndex].variableAccessData()->isCaptured()) { - if (destination.isTop()) - continue; - destination.makeTop(); - changed = true; - continue; - } changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.local(local), to->variablesAtHead.local(local), from->variablesAtTail.local(local)); } @@ -1702,8 +1688,6 @@ inline bool AbstractState::merge(BasicBlock* from, BasicBlock* to) inline bool AbstractState::mergeToSuccessors( Graph& graph, BasicBlock* basicBlock, BranchDirection branchDirection) { - PROFILE(FLAG_FOR_MERGE_TO_SUCCESSORS); - Node& terminal = graph[basicBlock->last()]; ASSERT(terminal.isTerminal()); diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.h b/Source/JavaScriptCore/dfg/DFGAbstractState.h index 4ce3df19b..4b0a248f3 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.h @@ -136,6 +136,11 @@ public: return forNode(nodeUse.index()); } + Operands<AbstractValue>& variables() + { + return m_variables; + } + // Call this before beginning CFA to initialize the abstract values of // arguments, and to indicate which blocks should be listed for CFA // execution. @@ -208,7 +213,8 @@ public: void dump(FILE* out); private: - void clobberStructures(unsigned); + void clobberWorld(const CodeOrigin&, unsigned indexInBlock); + void clobberStructures(unsigned indexInBlock); bool mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, NodeIndex); diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index 43157963c..358171029 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -142,20 +142,21 @@ private: { return getDirect(m_inlineStackTop->remapOperand(operand)); } - void setDirect(int operand, NodeIndex value) + enum SetMode { NormalSet, SetOnEntry }; + void setDirect(int operand, NodeIndex value, SetMode setMode = NormalSet) { // Is this an argument? if (operandIsArgument(operand)) { - setArgument(operand, value); + setArgument(operand, value, setMode); return; } // Must be a local. - setLocal((unsigned)operand, value); + setLocal((unsigned)operand, value, setMode); } - void set(int operand, NodeIndex value) + void set(int operand, NodeIndex value, SetMode setMode = NormalSet) { - setDirect(m_inlineStackTop->remapOperand(operand), value); + setDirect(m_inlineStackTop->remapOperand(operand), value, setMode); } NodeIndex injectLazyOperandPrediction(NodeIndex nodeIndex) @@ -236,37 +237,19 @@ private: return nodeIndex; } - void setLocal(unsigned operand, NodeIndex value) + void setLocal(unsigned operand, NodeIndex value, SetMode setMode = NormalSet) { bool isCaptured = m_codeBlock->localIsCaptured(m_inlineStackTop->m_inlineCallFrame, operand); + if (setMode == NormalSet) { + ArgumentPosition* argumentPosition = findArgumentPositionForLocal(operand); + if (isCaptured || argumentPosition) + flushDirect(operand, argumentPosition); + } + VariableAccessData* variableAccessData = newVariableAccessData(operand, isCaptured); NodeIndex nodeIndex = addToGraph(SetLocal, OpInfo(variableAccessData), value); m_currentBlock->variablesAtTail.local(operand) = nodeIndex; - - bool shouldFlush = isCaptured; - - if (!shouldFlush) { - // If this is in argument position, then it should be flushed. - for (InlineStackEntry* stack = m_inlineStackTop; ; stack = stack->m_caller) { - InlineCallFrame* inlineCallFrame = stack->m_inlineCallFrame; - if (!inlineCallFrame) - break; - if (static_cast<int>(operand) >= inlineCallFrame->stackOffset - RegisterFile::CallFrameHeaderSize) - continue; - if (static_cast<int>(operand) == inlineCallFrame->stackOffset + CallFrame::thisArgumentOffset()) - continue; - if (operand < inlineCallFrame->stackOffset - RegisterFile::CallFrameHeaderSize - inlineCallFrame->arguments.size()) - continue; - int argument = operandToArgument(operand - inlineCallFrame->stackOffset); - stack->m_argumentPositions[argument]->addVariable(variableAccessData); - shouldFlush = true; - break; - } - } - - if (shouldFlush) - addToGraph(Flush, OpInfo(variableAccessData), nodeIndex); } // Used in implementing get/set, above, where the operand is an argument. @@ -292,7 +275,7 @@ private: VariableAccessData* variableAccessData = flushChild.variableAccessData(); variableAccessData->mergeIsCaptured(isCaptured); nodeIndex = injectLazyOperandPrediction(addToGraph(GetLocal, OpInfo(variableAccessData), nodeIndex)); - m_currentBlock->variablesAtTail.local(operand) = nodeIndex; + m_currentBlock->variablesAtTail.argument(argument) = nodeIndex; return nodeIndex; } nodePtr = &flushChild; @@ -336,31 +319,70 @@ private: return nodeIndex; } - void setArgument(int operand, NodeIndex value) + void setArgument(int operand, NodeIndex value, SetMode setMode = NormalSet) { unsigned argument = operandToArgument(operand); bool isCaptured = m_codeBlock->argumentIsCaptured(argument); ASSERT(argument < m_numArguments); + // Always flush arguments, except for 'this'. + if (argument && setMode == NormalSet) + flushDirect(operand); + VariableAccessData* variableAccessData = newVariableAccessData(operand, isCaptured); - InlineStackEntry* stack = m_inlineStackTop; - while (stack->m_inlineCallFrame) // find the machine stack entry. - stack = stack->m_caller; - stack->m_argumentPositions[argument]->addVariable(variableAccessData); NodeIndex nodeIndex = addToGraph(SetLocal, OpInfo(variableAccessData), value); m_currentBlock->variablesAtTail.argument(argument) = nodeIndex; - // Always flush arguments, except for 'this'. - if (argument) - addToGraph(Flush, OpInfo(variableAccessData), nodeIndex); } - VariableAccessData* flushArgument(int operand) + ArgumentPosition* findArgumentPositionForArgument(int argument) + { + InlineStackEntry* stack = m_inlineStackTop; + while (stack->m_inlineCallFrame) + stack = stack->m_caller; + return stack->m_argumentPositions[argument]; + } + + ArgumentPosition* findArgumentPositionForLocal(int operand) + { + for (InlineStackEntry* stack = m_inlineStackTop; ; stack = stack->m_caller) { + InlineCallFrame* inlineCallFrame = stack->m_inlineCallFrame; + if (!inlineCallFrame) + break; + if (operand >= inlineCallFrame->stackOffset - RegisterFile::CallFrameHeaderSize) + continue; + if (operand == inlineCallFrame->stackOffset + CallFrame::thisArgumentOffset()) + continue; + if (static_cast<unsigned>(operand) < inlineCallFrame->stackOffset - RegisterFile::CallFrameHeaderSize - inlineCallFrame->arguments.size()) + continue; + int argument = operandToArgument(operand - inlineCallFrame->stackOffset); + return stack->m_argumentPositions[argument]; + } + return 0; + } + + ArgumentPosition* findArgumentPosition(int operand) + { + if (operandIsArgument(operand)) + return findArgumentPositionForArgument(operandToArgument(operand)); + return findArgumentPositionForLocal(operand); + } + + void flush(int operand) + { + flushDirect(m_inlineStackTop->remapOperand(operand)); + } + + void flushDirect(int operand) + { + flushDirect(operand, findArgumentPosition(operand)); + } + + void flushDirect(int operand, ArgumentPosition* argumentPosition) { // FIXME: This should check if the same operand had already been flushed to // some other local variable. - operand = m_inlineStackTop->remapOperand(operand); bool isCaptured = m_codeBlock->isCaptured(m_inlineStackTop->m_inlineCallFrame, operand); ASSERT(operand < FirstConstantRegisterIndex); @@ -398,7 +420,9 @@ private: VariableAccessData* variableAccessData = node.variableAccessData(); variableAccessData->mergeIsCaptured(isCaptured); addToGraph(Flush, OpInfo(variableAccessData), nodeIndex); - return variableAccessData; + if (argumentPosition) + argumentPosition->addVariable(variableAccessData); + return; } VariableAccessData* variableAccessData = newVariableAccessData(operand, isCaptured); @@ -413,7 +437,21 @@ private: m_currentBlock->variablesAtTail.local(index) = nodeIndex; m_currentBlock->variablesAtHead.setLocalFirstTime(index, nodeIndex); } - return variableAccessData; + if (argumentPosition) + argumentPosition->addVariable(variableAccessData); + } + + void flushArgumentsAndCapturedVariables() + { + int numArguments; + if (m_inlineStackTop->m_inlineCallFrame) + numArguments = m_inlineStackTop->m_inlineCallFrame->arguments.size(); + else + numArguments = m_inlineStackTop->m_codeBlock->numParameters(); + for (unsigned argument = numArguments; argument-- > 1;) + flush(argumentToOperand(argument)); + for (unsigned local = m_inlineStackTop->m_codeBlock->m_numCapturedVars; local--;) + flush(local); } // Get an operand, and perform a ToInt32/ToNumber conversion on it. @@ -1219,17 +1257,6 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c // FIXME: Don't flush constants! - Vector<VariableAccessData*, 8> arguments; - for (int i = 1; i < argumentCountIncludingThis; ++i) { - VariableAccessData* variableAccessData = - flushArgument(registerOffset + argumentToOperand(i)); - arguments.append(variableAccessData); - - // Are we going to be capturing arguments? If so make sure we record this fact. - if (codeBlock->argumentIsCaptured(i)) - variableAccessData->mergeIsCaptured(true); - } - int inlineCallFrameStart = m_inlineStackTop->remapOperand(registerOffset) - RegisterFile::CallFrameHeaderSize; // Make sure that the area used by the call frame is reserved. @@ -1251,13 +1278,6 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c usesResult ? resultOperand : InvalidVirtualRegister), (VirtualRegister)inlineCallFrameStart, argumentCountIncludingThis, kind); - // Link up the argument variable access datas to their argument positions. - for (int i = 1; i < argumentCountIncludingThis; ++i) { - if (static_cast<size_t>(i) >= inlineStackEntry.m_argumentPositions.size()) - break; - inlineStackEntry.m_argumentPositions[i]->addVariable(arguments[i - 1]); - } - // This is where the actual inlining really happens. unsigned oldIndex = m_currentIndex; unsigned oldProfilingIndex = m_currentProfilingIndex; @@ -1578,7 +1598,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) case op_enter: // Initialize all locals to undefined. for (int i = 0; i < m_inlineStackTop->m_codeBlock->m_numVars; ++i) - set(i, constantUndefined()); + set(i, constantUndefined(), SetOnEntry); NEXT_OPCODE(op_enter); case op_convert_this: { @@ -2352,6 +2372,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) } case op_ret: + flushArgumentsAndCapturedVariables(); if (m_inlineStackTop->m_inlineCallFrame) { if (m_inlineStackTop->m_returnValue != InvalidVirtualRegister) setDirect(m_inlineStackTop->m_returnValue, get(currentInstruction[1].u.operand)); @@ -2379,15 +2400,18 @@ bool ByteCodeParser::parseBlock(unsigned limit) LAST_OPCODE(op_ret); case op_end: + flushArgumentsAndCapturedVariables(); ASSERT(!m_inlineStackTop->m_inlineCallFrame); addToGraph(Return, get(currentInstruction[1].u.operand)); LAST_OPCODE(op_end); case op_throw: + flushArgumentsAndCapturedVariables(); addToGraph(Throw, get(currentInstruction[1].u.operand)); LAST_OPCODE(op_throw); case op_throw_reference_error: + flushArgumentsAndCapturedVariables(); addToGraph(ThrowReferenceError); LAST_OPCODE(op_throw_reference_error); @@ -2818,8 +2842,8 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry( , m_didEarlyReturn(false) , m_caller(byteCodeParser->m_inlineStackTop) { - m_argumentPositions.resize(codeBlock->numParameters()); - for (unsigned i = codeBlock->numParameters(); i--;) { + m_argumentPositions.resize(argumentCountIncludingThis); + for (unsigned i = argumentCountIncludingThis; i--;) { byteCodeParser->m_graph.m_argumentPositions.append(ArgumentPosition()); ArgumentPosition* argumentPosition = &byteCodeParser->m_graph.m_argumentPositions.last(); m_argumentPositions[i] = argumentPosition; diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index 3eeb70e05..31488cb1c 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -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 @@ -620,7 +620,7 @@ private: } // This returns the Flush node that is keeping a SetLocal alive. - NodeIndex setLocalStoreElimination(VirtualRegister local) + NodeIndex setLocalStoreElimination(VirtualRegister local, NodeIndex expectedNodeIndex) { for (unsigned i = m_indexInBlock; i--;) { NodeIndex index = m_currentBlock->at(i); @@ -629,7 +629,7 @@ private: continue; switch (node.op()) { case GetLocal: - case SetLocal: + case Flush: if (node.local() == local) return NoNode; break; @@ -639,19 +639,13 @@ private: return NoNode; break; - case Flush: { + case SetLocal: { if (node.local() != local) break; - if (!i) - break; - NodeIndex prevIndex = m_currentBlock->at(i - 1); - if (prevIndex != node.child1().index()) - break; - ASSERT(m_graph[prevIndex].local() == local); - ASSERT(m_graph[prevIndex].variableAccessData() == node.variableAccessData()); - ASSERT(m_graph[prevIndex].shouldGenerate()); - if (m_graph[prevIndex].refCount() > 1) - break; + if (index != expectedNodeIndex) + return NoNode; + if (m_graph[index].refCount() > 1) + return NoNode; return index; } @@ -660,6 +654,14 @@ private: return NoNode; break; + case TearOffActivation: + case TearOffArguments: + // If an activation is being torn off then it means that captured variables + // are live. We could be clever here and check if the local qualifies as an + // argument register. But that seems like it would buy us very little since + // any kind of tear offs are rare to begin with. + return NoNode; + default: if (m_graph.clobbersWorld(index)) return NoNode; @@ -855,27 +857,26 @@ private: break; } - case SetLocal: { + case Flush: { if (m_fixpointState == FixpointNotConverged) break; VariableAccessData* variableAccessData = node.variableAccessData(); if (!variableAccessData->isCaptured()) break; VirtualRegister local = variableAccessData->local(); - NodeIndex replacementIndex = setLocalStoreElimination(local); + NodeIndex replacementIndex = setLocalStoreElimination(local, node.child1().index()); if (replacementIndex == NoNode) break; Node& replacement = m_graph[replacementIndex]; - ASSERT(replacement.op() == Flush); + ASSERT(replacement.op() == SetLocal); ASSERT(replacement.refCount() == 1); ASSERT(replacement.shouldGenerate()); - ASSERT(replacement.mustGenerate()); - replacement.setOpAndDefaultFlags(Phantom); - NodeIndex setLocalIndex = replacement.child1().index(); - ASSERT(m_graph[setLocalIndex].op() == SetLocal); - m_graph.clearAndDerefChild1(replacement); - replacement.children.child1() = m_graph[setLocalIndex].child1(); - m_graph.ref(replacement.child1()); + node.setOpAndDefaultFlags(Phantom); + NodeIndex dataNodeIndex = replacement.child1().index(); + ASSERT(m_graph[dataNodeIndex].hasResult()); + m_graph.clearAndDerefChild1(node); + node.children.child1() = Edge(dataNodeIndex); + m_graph.ref(dataNodeIndex); break; } diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp index b2b74ba04..1e75ddea1 100644 --- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp @@ -77,13 +77,42 @@ public: ASSERT(m_graph[node.child1()].op() == Phi); ASSERT(!m_graph[node.child1()].hasResult()); - ASSERT(block->variablesAtHead.operand(node.local()) == nodeIndex); - ASSERT(block->isInPhis(node.child1().index())); - block->variablesAtHead.operand(node.local()) = node.child1().index(); + NodeIndex previousLocalAccess = NoNode; + if (block->variablesAtHead.operand(node.local()) == nodeIndex) { + // We expect this to be the common case. + ASSERT(block->isInPhis(node.child1().index())); + previousLocalAccess = node.child1().index(); + block->variablesAtHead.operand(node.local()) = previousLocalAccess; + } else { + ASSERT(indexInBlock > 0); + // Must search for the previous access to this local. + for (BlockIndex subIndexInBlock = indexInBlock - 1; subIndexInBlock--;) { + NodeIndex subNodeIndex = block->at(subIndexInBlock); + Node& subNode = m_graph[subNodeIndex]; + if (!subNode.shouldGenerate()) + continue; + if (!subNode.hasVariableAccessData()) + continue; + if (subNode.local() != node.local()) + continue; + // The two must have been unified. + ASSERT(subNode.variableAccessData() == node.variableAccessData()); + // Currently, the previous node must be a flush. + // NOTE: This assertion should be removed if we ever do + // constant folding on captured variables. In particular, + // this code does not require the previous node to be a flush, + // but we are asserting this anyway because it is a constraint + // of the IR and this is as good a place as any to assert it. + ASSERT(subNode.op() == Flush); + previousLocalAccess = subNodeIndex; + break; + } + ASSERT(previousLocalAccess != NoNode); + } NodeIndex tailNodeIndex = block->variablesAtTail.operand(node.local()); if (tailNodeIndex == nodeIndex) - block->variablesAtTail.operand(node.local()) = node.child1().index(); + block->variablesAtTail.operand(node.local()) = previousLocalAccess; else { ASSERT(m_graph[tailNodeIndex].op() == Flush || m_graph[tailNodeIndex].op() == SetLocal); diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.h b/Source/JavaScriptCore/dfg/DFGJITCompiler.h index 360165b24..d3ff3be07 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.h +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.h @@ -304,6 +304,10 @@ public: void noticeOSREntry(BasicBlock& basicBlock, JITCompiler::Label blockHead, LinkBuffer& linkBuffer) { #if DFG_ENABLE(OSR_ENTRY) + // OSR entry is not allowed into blocks deemed unreachable by control flow analysis. + if (!basicBlock.cfaHasVisited) + return; + OSREntryData* entry = codeBlock()->appendDFGOSREntryData(basicBlock.bytecodeBegin, linkBuffer.offsetOf(blockHead)); entry->m_expectedValues = basicBlock.valuesAtHead; diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index f95b993d7..b5ac4601a 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -34,6 +34,7 @@ #include "GetterSetter.h" #include <wtf/InlineASM.h> #include "Interpreter.h" +#include "JITExceptions.h" #include "JSActivation.h" #include "JSGlobalData.h" #include "JSStaticScopeObject.h" @@ -1149,35 +1150,31 @@ DFGHandlerEncoded DFG_OPERATION lookupExceptionHandler(ExecState* exec, uint32_t { JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); - + JSValue exceptionValue = exec->exception(); ASSERT(exceptionValue); - + unsigned vPCIndex = exec->codeBlock()->bytecodeOffsetForCallAtIndex(callIndex); - HandlerInfo* handler = exec->globalData().interpreter->throwException(exec, exceptionValue, vPCIndex); - - void* catchRoutine = handler ? handler->nativeCode.executableAddress() : (void*)ctiOpThrowNotCaught; - ASSERT(catchRoutine); - return dfgHandlerEncoded(exec, catchRoutine); + ExceptionHandler handler = genericThrow(globalData, exec, exceptionValue, vPCIndex); + ASSERT(handler.catchRoutine); + return dfgHandlerEncoded(handler.callFrame, handler.catchRoutine); } DFGHandlerEncoded DFG_OPERATION lookupExceptionHandlerInStub(ExecState* exec, StructureStubInfo* stubInfo) { JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); - + JSValue exceptionValue = exec->exception(); ASSERT(exceptionValue); CodeOrigin codeOrigin = stubInfo->codeOrigin; while (codeOrigin.inlineCallFrame) codeOrigin = codeOrigin.inlineCallFrame->caller; - - HandlerInfo* handler = exec->globalData().interpreter->throwException(exec, exceptionValue, codeOrigin.bytecodeIndex); - - void* catchRoutine = handler ? handler->nativeCode.executableAddress() : (void*)ctiOpThrowNotCaught; - ASSERT(catchRoutine); - return dfgHandlerEncoded(exec, catchRoutine); + + ExceptionHandler handler = genericThrow(globalData, exec, exceptionValue, codeOrigin.bytecodeIndex); + ASSERT(handler.catchRoutine); + return dfgHandlerEncoded(handler.callFrame, handler.catchRoutine); } double DFG_OPERATION dfgConvertJSValueToNumber(ExecState* exec, EncodedJSValue value) diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index caa21aabf..9b82121b3 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -967,6 +967,16 @@ void SpeculativeJIT::compile(BasicBlock& block) if (!block.isReachable) return; + + if (!block.cfaHasVisited) { + // Don't generate code for basic blocks that are unreachable according to CFA. + // But to be sure that nobody has generated a jump to this block, drop in a + // breakpoint here. +#if !ASSERT_DISABLED + m_jit.breakpoint(); +#endif + return; + } m_blockHeads[m_block] = m_jit.label(); #if DFG_ENABLE(JIT_BREAK_ON_EVERY_BLOCK) @@ -990,20 +1000,18 @@ 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); - // FIXME: Use the variable access data, not the first node in the block. - // https://bugs.webkit.org/show_bug.cgi?id=87205 - if (m_jit.codeBlock()->localIsCaptured(at(block[0]).codeOrigin.inlineCallFrame, i)) - m_variables[i] = ValueSource(ValueInRegisterFile); - else if (nodeIndex == NoNode) + if (nodeIndex == NoNode) m_variables[i] = ValueSource(SourceIsDead); else if (at(nodeIndex).variableAccessData()->isArgumentsAlias()) m_variables[i] = ValueSource(ArgumentsSource); + else if (at(nodeIndex).variableAccessData()->isCaptured()) + m_variables[i] = ValueSource(ValueInRegisterFile); else if (!at(nodeIndex).refCount()) m_variables[i] = ValueSource(SourceIsDead); else if (at(nodeIndex).variableAccessData()->shouldUseDoubleFormat()) m_variables[i] = ValueSource(DoubleInRegisterFile); else - m_variables[i] = ValueSource::forPrediction(at(nodeIndex).variableAccessData()->prediction()); + m_variables[i] = ValueSource::forPrediction(at(nodeIndex).variableAccessData()->argumentAwarePrediction()); } m_lastSetOperand = std::numeric_limits<int>::max(); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 912078a79..56a1a1861 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -2191,22 +2191,23 @@ public: unsigned setLocalIndexInBlock = m_indexInBlock + 1; Node* setLocal = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock)); + bool hadInt32ToDouble = false; if (setLocal->op() == Int32ToDouble) { setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock)); + hadInt32ToDouble = true; + } + if (setLocal->op() == Flush) + setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock)); + + if (hadInt32ToDouble) ASSERT(at(setLocal->child1()).child1() == m_compileIndex); - } else + else ASSERT(setLocal->child1() == m_compileIndex); - ASSERT(setLocal->op() == SetLocal); ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin); Node* nextNode = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 1)); - if (nextNode->codeOrigin == at(m_compileIndex).codeOrigin) { - ASSERT(nextNode->op() == Flush); - nextNode = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 2)); - ASSERT(nextNode->codeOrigin != at(m_compileIndex).codeOrigin); // duplicate the same assertion as below so that if we fail, we'll know we came down this path. - } ASSERT(nextNode->codeOrigin != at(m_compileIndex).codeOrigin); OSRExit& exit = m_jit.codeBlock()->lastOSRExit(); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 6c0093e41..00a83000a 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -3858,12 +3858,16 @@ void SpeculativeJIT::compile(Node& node) } case CheckArgumentsNotCreated: { - speculationCheck( - Uncountable, JSValueRegs(), NoNode, - m_jit.branch32( - JITCompiler::NotEqual, - JITCompiler::tagFor(m_jit.argumentsRegisterFor(node.codeOrigin)), - TrustedImm32(JSValue::EmptyValueTag))); + if (!isEmptyPrediction( + m_state.variables().operand( + m_jit.graph().argumentsRegisterFor(node.codeOrigin)).m_type)) { + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branch32( + JITCompiler::NotEqual, + JITCompiler::tagFor(m_jit.argumentsRegisterFor(node.codeOrigin)), + TrustedImm32(JSValue::EmptyValueTag))); + } noResult(m_compileIndex); break; } @@ -3872,12 +3876,16 @@ void SpeculativeJIT::compile(Node& node) GPRTemporary result(this); GPRReg resultGPR = result.gpr(); - speculationCheck( - ArgumentsEscaped, JSValueRegs(), NoNode, - m_jit.branch32( - JITCompiler::NotEqual, - JITCompiler::tagFor(m_jit.argumentsRegisterFor(node.codeOrigin)), - TrustedImm32(JSValue::EmptyValueTag))); + if (!isEmptyPrediction( + m_state.variables().operand( + m_jit.graph().argumentsRegisterFor(node.codeOrigin)).m_type)) { + speculationCheck( + ArgumentsEscaped, JSValueRegs(), NoNode, + m_jit.branch32( + JITCompiler::NotEqual, + JITCompiler::tagFor(m_jit.argumentsRegisterFor(node.codeOrigin)), + TrustedImm32(JSValue::EmptyValueTag))); + } ASSERT(!node.codeOrigin.inlineCallFrame); m_jit.load32(JITCompiler::payloadFor(RegisterFile::ArgumentCount), resultGPR); @@ -3929,12 +3937,16 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultPayloadGPR = resultPayload.gpr(); GPRReg resultTagGPR = resultTag.gpr(); - speculationCheck( - ArgumentsEscaped, JSValueRegs(), NoNode, - m_jit.branch32( - JITCompiler::NotEqual, - JITCompiler::tagFor(m_jit.argumentsRegisterFor(node.codeOrigin)), - TrustedImm32(JSValue::EmptyValueTag))); + if (!isEmptyPrediction( + m_state.variables().operand( + m_jit.graph().argumentsRegisterFor(node.codeOrigin)).m_type)) { + speculationCheck( + ArgumentsEscaped, JSValueRegs(), NoNode, + m_jit.branch32( + JITCompiler::NotEqual, + JITCompiler::tagFor(m_jit.argumentsRegisterFor(node.codeOrigin)), + TrustedImm32(JSValue::EmptyValueTag))); + } m_jit.add32(TrustedImm32(1), indexGPR, resultPayloadGPR); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index e4939b23a..ca57743a6 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -3880,12 +3880,16 @@ void SpeculativeJIT::compile(Node& node) GPRTemporary result(this); GPRReg resultGPR = result.gpr(); - speculationCheck( - ArgumentsEscaped, JSValueRegs(), NoNode, - m_jit.branchTestPtr( - JITCompiler::NonZero, - JITCompiler::addressFor( - m_jit.argumentsRegisterFor(node.codeOrigin)))); + if (!isEmptyPrediction( + m_state.variables().operand( + m_jit.graph().argumentsRegisterFor(node.codeOrigin)).m_type)) { + speculationCheck( + ArgumentsEscaped, JSValueRegs(), NoNode, + m_jit.branchTestPtr( + JITCompiler::NonZero, + JITCompiler::addressFor( + m_jit.argumentsRegisterFor(node.codeOrigin)))); + } ASSERT(!node.codeOrigin.inlineCallFrame); m_jit.load32(JITCompiler::payloadFor(RegisterFile::ArgumentCount), resultGPR); @@ -3935,12 +3939,16 @@ void SpeculativeJIT::compile(Node& node) GPRReg indexGPR = index.gpr(); GPRReg resultGPR = result.gpr(); - speculationCheck( - ArgumentsEscaped, JSValueRegs(), NoNode, - m_jit.branchTestPtr( - JITCompiler::NonZero, - JITCompiler::addressFor( - m_jit.argumentsRegisterFor(node.codeOrigin)))); + if (!isEmptyPrediction( + m_state.variables().operand( + m_jit.graph().argumentsRegisterFor(node.codeOrigin)).m_type)) { + speculationCheck( + ArgumentsEscaped, JSValueRegs(), NoNode, + m_jit.branchTestPtr( + JITCompiler::NonZero, + JITCompiler::addressFor( + m_jit.argumentsRegisterFor(node.codeOrigin)))); + } m_jit.add32(TrustedImm32(1), indexGPR, resultGPR); if (node.codeOrigin.inlineCallFrame) { @@ -4024,12 +4032,16 @@ void SpeculativeJIT::compile(Node& node) } case CheckArgumentsNotCreated: { - speculationCheck( - ArgumentsEscaped, JSValueRegs(), NoNode, - m_jit.branchTestPtr( - JITCompiler::NonZero, - JITCompiler::addressFor( - m_jit.argumentsRegisterFor(node.codeOrigin)))); + if (!isEmptyPrediction( + m_state.variables().operand( + m_jit.graph().argumentsRegisterFor(node.codeOrigin)).m_type)) { + speculationCheck( + ArgumentsEscaped, JSValueRegs(), NoNode, + m_jit.branchTestPtr( + JITCompiler::NonZero, + JITCompiler::addressFor( + m_jit.argumentsRegisterFor(node.codeOrigin)))); + } noResult(m_compileIndex); break; } |