void doRoundOfDoubleVoting() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog("Voting on double uses of locals [%u]\n", m_count); #endif for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) m_graph.m_variableAccessData[i].find()->clearVotes(); for (m_compileIndex = 0; m_compileIndex < m_graph.size(); ++m_compileIndex) { Node& node = m_graph[m_compileIndex]; switch (node.op()) { case ValueAdd: case ArithAdd: case ArithSub: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); DoubleBallot ballot; if (isNumberSpeculation(left) && isNumberSpeculation(right) && !m_graph.addShouldSpeculateInteger(node)) ballot = VoteDouble; else ballot = VoteValue; m_graph.vote(node.child1(), ballot); m_graph.vote(node.child2(), ballot); break; } case ArithMul: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); DoubleBallot ballot; if (isNumberSpeculation(left) && isNumberSpeculation(right) && !m_graph.mulShouldSpeculateInteger(node)) ballot = VoteDouble; else ballot = VoteValue; m_graph.vote(node.child1(), ballot); m_graph.vote(node.child2(), ballot); break; } case ArithMin: case ArithMax: case ArithMod: case ArithDiv: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); DoubleBallot ballot; if (isNumberSpeculation(left) && isNumberSpeculation(right) && !(Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child1()]) && node.canSpeculateInteger())) ballot = VoteDouble; else ballot = VoteValue; m_graph.vote(node.child1(), ballot); m_graph.vote(node.child2(), ballot); break; } case ArithAbs: DoubleBallot ballot; if (!(m_graph[node.child1()].shouldSpeculateInteger() && node.canSpeculateInteger())) ballot = VoteDouble; else ballot = VoteValue; m_graph.vote(node.child1(), ballot); break; case ArithSqrt: m_graph.vote(node.child1(), VoteDouble); break; case SetLocal: { SpeculatedType prediction = m_graph[node.child1()].prediction(); if (isDoubleSpeculation(prediction)) node.variableAccessData()->vote(VoteDouble); else if (!isNumberSpeculation(prediction) || isInt32Speculation(prediction)) node.variableAccessData()->vote(VoteValue); break; } default: m_graph.vote(node, VoteValue); break; } } for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) { VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i]; if (!variableAccessData->isRoot()) continue; if (operandIsArgument(variableAccessData->local()) || variableAccessData->isCaptured()) continue; m_changed |= variableAccessData->tallyVotesForShouldUseDoubleFormat(); } for (unsigned i = 0; i < m_graph.m_argumentPositions.size(); ++i) m_changed |= m_graph.m_argumentPositions[i].mergeArgumentAwareness(); for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) { VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i]; if (!variableAccessData->isRoot()) continue; if (operandIsArgument(variableAccessData->local()) || variableAccessData->isCaptured()) continue; m_changed |= variableAccessData->makePredictionForDoubleFormat(); } }
bool run() { for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; variable->clearVotes(); } // Identify the set of variables that are always subject to the same structure // checks. For now, only consider monomorphic structure checks (one structure). for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { NodeIndex nodeIndex = block->at(indexInBlock); Node& node = m_graph[nodeIndex]; if (!node.shouldGenerate()) continue; switch (node.op()) { case CheckStructure: { Node& child = m_graph[node.child1()]; if (child.op() != GetLocal) break; VariableAccessData* variable = child.variableAccessData(); variable->vote(VoteStructureCheck); if (variable->isCaptured() || variable->structureCheckHoistingFailed()) break; if (!isCellSpeculation(variable->prediction())) break; noticeStructureCheck(variable, node.structureSet()); break; } case ForwardCheckStructure: case ForwardStructureTransitionWatchpoint: // We currently rely on the fact that we're the only ones who would // insert this node. ASSERT_NOT_REACHED(); break; case GetByOffset: case PutByOffset: case PutStructure: case StructureTransitionWatchpoint: case AllocatePropertyStorage: case ReallocatePropertyStorage: case GetPropertyStorage: case GetByVal: case PutByVal: case PutByValAlias: case GetArrayLength: case CheckArray: case GetIndexedPropertyStorage: case Phantom: // Don't count these uses. break; default: m_graph.vote(node, VoteOther); break; } } } // Disable structure hoisting on variables that appear to mostly be used in // contexts where it doesn't make sense. for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; if (variable->voteRatio() >= Options::structureCheckVoteRatioForHoisting()) continue; HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) continue; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog("Zeroing the structure to hoist for %s because the ratio is %lf.\n", m_graph.nameOfVariableAccessData(variable), variable->voteRatio()); #endif iter->second.m_structure = 0; } // Identify the set of variables that are live across a structure clobber. Operands<VariableAccessData*> live( m_graph.m_blocks[0]->variablesAtTail.numberOfArguments(), m_graph.m_blocks[0]->variablesAtTail.numberOfLocals()); for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; ASSERT(live.numberOfArguments() == block->variablesAtTail.numberOfArguments()); ASSERT(live.numberOfLocals() == block->variablesAtTail.numberOfLocals()); for (unsigned i = live.size(); i--;) { NodeIndex indexAtTail = block->variablesAtTail[i]; VariableAccessData* variable; if (indexAtTail == NoNode) variable = 0; else variable = m_graph[indexAtTail].variableAccessData(); live[i] = variable; } for (unsigned indexInBlock = block->size(); indexInBlock--;) { NodeIndex nodeIndex = block->at(indexInBlock); Node& node = m_graph[nodeIndex]; if (!node.shouldGenerate()) continue; switch (node.op()) { case GetLocal: case Flush: // This is a birth. live.operand(node.local()) = node.variableAccessData(); break; case SetLocal: case SetArgument: ASSERT(live.operand(node.local())); // Must be live. ASSERT(live.operand(node.local()) == node.variableAccessData()); // Must have the variable we expected. // This is a death. live.operand(node.local()) = 0; break; // Use the CFA's notion of what clobbers the world. case ValueAdd: if (m_graph.addShouldSpeculateInteger(node)) break; if (Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) break; clobber(live); break; case CompareLess: case CompareLessEq: case CompareGreater: case CompareGreaterEq: case CompareEq: { Node& left = m_graph[node.child1()]; Node& right = m_graph[node.child2()]; if (Node::shouldSpeculateInteger(left, right)) break; if (Node::shouldSpeculateNumber(left, right)) break; if (node.op() == CompareEq) { if ((m_graph.isConstant(node.child1().index()) && m_graph.valueOfJSConstant(node.child1().index()).isNull()) || (m_graph.isConstant(node.child2().index()) && m_graph.valueOfJSConstant(node.child2().index()).isNull())) break; if (Node::shouldSpeculateFinalObject(left, right)) break; if (Node::shouldSpeculateArray(left, right)) break; if (left.shouldSpeculateFinalObject() && right.shouldSpeculateFinalObjectOrOther()) break; if (right.shouldSpeculateFinalObject() && left.shouldSpeculateFinalObjectOrOther()) break; if (left.shouldSpeculateArray() && right.shouldSpeculateArrayOrOther()) break; if (right.shouldSpeculateArray() && left.shouldSpeculateArrayOrOther()) break; } clobber(live); break; } case GetByVal: case PutByVal: case PutByValAlias: if (m_graph.byValIsPure(node)) break; clobber(live); break; case GetMyArgumentsLengthSafe: case GetMyArgumentByValSafe: case GetById: case GetByIdFlush: case PutStructure: case PhantomPutStructure: case PutById: case PutByIdDirect: case Call: case Construct: case Resolve: case ResolveBase: case ResolveBaseStrictPut: case ResolveGlobal: clobber(live); break; default: ASSERT(node.op() != Phi); break; } } } bool changed = false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin(); it != m_map.end(); ++it) { if (!it->second.m_structure) { dataLog("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->first)); continue; } if (it->second.m_isClobbered && !it->second.m_structure->transitionWatchpointSetIsStillValid()) { dataLog("Not hoisting checks for %s because the structure is clobbered and has an invalid watchpoint set.\n", m_graph.nameOfVariableAccessData(it->first)); continue; } dataLog("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->first)); } #endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) // Make changes: // 1) If a variable's live range does not span a clobber, then inject structure // checks before the SetLocal. // 2) If a variable's live range spans a clobber but is watchpointable, then // inject structure checks before the SetLocal and replace all other structure // checks on that variable with structure transition watchpoints. InsertionSet<NodeIndex> insertionSet; for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { NodeIndex nodeIndex = block->at(indexInBlock); Node& node = m_graph[nodeIndex]; // Be careful not to use 'node' after appending to the graph. In those switch // cases where we need to append, we first carefully extract everything we need // from the node, before doing any appending. if (!node.shouldGenerate()) continue; switch (node.op()) { case SetArgument: { ASSERT(!blockIndex); // Insert a GetLocal and a CheckStructure immediately following this // SetArgument, if the variable was a candidate for structure hoisting. // If the basic block previously only had the SetArgument as its // variable-at-tail, then replace it with this GetLocal. VariableAccessData* variable = node.variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) break; if (!iter->second.m_structure) break; if (iter->second.m_isClobbered && !iter->second.m_structure->transitionWatchpointSetIsStillValid()) break; node.ref(); CodeOrigin codeOrigin = node.codeOrigin; Node getLocal(GetLocal, codeOrigin, OpInfo(variable), nodeIndex); getLocal.predict(variable->prediction()); getLocal.ref(); NodeIndex getLocalIndex = m_graph.size(); m_graph.append(getLocal); insertionSet.append(indexInBlock + 1, getLocalIndex); Node checkStructure(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->second.m_structure)), getLocalIndex); checkStructure.ref(); NodeIndex checkStructureIndex = m_graph.size(); m_graph.append(checkStructure); insertionSet.append(indexInBlock + 1, checkStructureIndex); if (block->variablesAtTail.operand(variable->local()) == nodeIndex) block->variablesAtTail.operand(variable->local()) = getLocalIndex; m_graph.substituteGetLocal(*block, indexInBlock, variable, getLocalIndex); changed = true; break; } case SetLocal: { VariableAccessData* variable = node.variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) break; if (!iter->second.m_structure) break; if (iter->second.m_isClobbered && !iter->second.m_structure->transitionWatchpointSetIsStillValid()) break; // First insert a dead SetLocal to tell OSR that the child's value should // be dropped into this bytecode variable if the CheckStructure decides // to exit. CodeOrigin codeOrigin = node.codeOrigin; NodeIndex child1 = node.child1().index(); Node setLocal(SetLocal, codeOrigin, OpInfo(variable), child1); NodeIndex setLocalIndex = m_graph.size(); m_graph.append(setLocal); insertionSet.append(indexInBlock, setLocalIndex); m_graph[child1].ref(); // Use a ForwardCheckStructure to indicate that we should exit to the // next bytecode instruction rather than reexecuting the current one. Node checkStructure(ForwardCheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->second.m_structure)), child1); checkStructure.ref(); NodeIndex checkStructureIndex = m_graph.size(); m_graph.append(checkStructure); insertionSet.append(indexInBlock, checkStructureIndex); changed = true; break; } case CheckStructure: { Node& child = m_graph[node.child1()]; if (child.op() != GetLocal) break; HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(child.variableAccessData()); if (iter == m_map.end()) break; if (!iter->second.m_structure) break; if (!iter->second.m_isClobbered) { node.setOpAndDefaultFlags(Phantom); ASSERT(node.refCount() == 1); break; } if (!iter->second.m_structure->transitionWatchpointSetIsStillValid()) break; ASSERT(iter->second.m_structure == node.structureSet().singletonStructure()); node.convertToStructureTransitionWatchpoint(); changed = true; break; } default: break; } } insertionSet.execute(*block); } return changed; }
bool run() { // This enumerates the locals that we actually care about and packs them. So for example // if we use local 1, 3, 4, 5, 7, then we remap them: 1->0, 3->1, 4->2, 5->3, 7->4. We // treat a variable as being "used" if there exists an access to it (SetLocal, GetLocal, // Flush, PhantomLocal). BitVector usedLocals; // Collect those variables that are used from IR. bool hasNodesThatNeedFixup = false; for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; for (unsigned nodeIndex = block->size(); nodeIndex--;) { Node* node = block->at(nodeIndex); switch (node->op()) { case GetLocal: case SetLocal: case Flush: case PhantomLocal: { VariableAccessData* variable = node->variableAccessData(); if (variable->local().isArgument()) break; usedLocals.set(variable->local().toLocal()); break; } case GetLocalUnlinked: { VirtualRegister operand = node->unlinkedLocal(); if (operand.isArgument()) break; usedLocals.set(operand.toLocal()); hasNodesThatNeedFixup = true; break; } case LoadVarargs: case ForwardVarargs: { LoadVarargsData* data = node->loadVarargsData(); if (data->count.isLocal()) usedLocals.set(data->count.toLocal()); if (data->start.isLocal()) { // This part really relies on the contiguity of stack layout // assignments. ASSERT(VirtualRegister(data->start.offset() + data->limit - 1).isLocal()); for (unsigned i = data->limit; i--;) usedLocals.set(VirtualRegister(data->start.offset() + i).toLocal()); } // the else case shouldn't happen. hasNodesThatNeedFixup = true; break; } case PutStack: case GetStack: { StackAccessData* stack = node->stackAccessData(); if (stack->local.isArgument()) break; usedLocals.set(stack->local.toLocal()); break; } default: break; } } } for (InlineCallFrameSet::iterator iter = m_graph.m_plan.inlineCallFrames->begin(); !!iter; ++iter) { InlineCallFrame* inlineCallFrame = *iter; if (inlineCallFrame->isVarargs()) { usedLocals.set(VirtualRegister( JSStack::ArgumentCount + inlineCallFrame->stackOffset).toLocal()); } for (unsigned argument = inlineCallFrame->arguments.size(); argument-- > 1;) { usedLocals.set(VirtualRegister( virtualRegisterForArgument(argument).offset() + inlineCallFrame->stackOffset).toLocal()); } } Vector<unsigned> allocation(usedLocals.size()); m_graph.m_nextMachineLocal = 0; for (unsigned i = 0; i < usedLocals.size(); ++i) { if (!usedLocals.get(i)) { allocation[i] = UINT_MAX; continue; } allocation[i] = m_graph.m_nextMachineLocal++; } for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; if (variable->local().isArgument()) { variable->machineLocal() = variable->local(); continue; } size_t local = variable->local().toLocal(); if (local >= allocation.size()) continue; if (allocation[local] == UINT_MAX) continue; variable->machineLocal() = assign(allocation, variable->local()); } for (StackAccessData* data : m_graph.m_stackAccessData) { if (!data->local.isLocal()) { data->machineLocal = data->local; continue; } if (static_cast<size_t>(data->local.toLocal()) >= allocation.size()) continue; if (allocation[data->local.toLocal()] == UINT_MAX) continue; data->machineLocal = assign(allocation, data->local); } // This register is never valid for DFG code blocks. codeBlock()->setActivationRegister(VirtualRegister()); if (LIKELY(!m_graph.hasDebuggerEnabled())) codeBlock()->setScopeRegister(VirtualRegister()); else codeBlock()->setScopeRegister(assign(allocation, codeBlock()->scopeRegister())); for (unsigned i = m_graph.m_inlineVariableData.size(); i--;) { InlineVariableData data = m_graph.m_inlineVariableData[i]; InlineCallFrame* inlineCallFrame = data.inlineCallFrame; if (inlineCallFrame->isVarargs()) { inlineCallFrame->argumentCountRegister = assign( allocation, VirtualRegister(inlineCallFrame->stackOffset + JSStack::ArgumentCount)); } for (unsigned argument = inlineCallFrame->arguments.size(); argument-- > 1;) { ArgumentPosition& position = m_graph.m_argumentPositions[ data.argumentPositionStart + argument]; VariableAccessData* variable = position.someVariable(); ValueSource source; if (!variable) source = ValueSource(SourceIsDead); else { source = ValueSource::forFlushFormat( variable->machineLocal(), variable->flushFormat()); } inlineCallFrame->arguments[argument] = source.valueRecovery(); } RELEASE_ASSERT(inlineCallFrame->isClosureCall == !!data.calleeVariable); if (inlineCallFrame->isClosureCall) { VariableAccessData* variable = data.calleeVariable->find(); ValueSource source = ValueSource::forFlushFormat( variable->machineLocal(), variable->flushFormat()); inlineCallFrame->calleeRecovery = source.valueRecovery(); } else RELEASE_ASSERT(inlineCallFrame->calleeRecovery.isConstant()); } // Fix GetLocalUnlinked's variable references. if (hasNodesThatNeedFixup) { for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; for (unsigned nodeIndex = block->size(); nodeIndex--;) { Node* node = block->at(nodeIndex); switch (node->op()) { case GetLocalUnlinked: { node->setUnlinkedMachineLocal(assign(allocation, node->unlinkedLocal())); break; } case LoadVarargs: case ForwardVarargs: { LoadVarargsData* data = node->loadVarargsData(); data->machineCount = assign(allocation, data->count); data->machineStart = assign(allocation, data->start); break; } default: break; } } } } return true; }
bool run() { ASSERT(m_graph.m_form == ThreadedCPS); for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; variable->clearVotes(); } // Identify the set of variables that are always subject to the same structure // checks. For now, only consider monomorphic structure checks (one structure). for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { Node* node = block->at(indexInBlock); switch (node->op()) { case CheckStructure: case StructureTransitionWatchpoint: { Node* child = node->child1().node(); if (child->op() != GetLocal) break; VariableAccessData* variable = child->variableAccessData(); variable->vote(VoteStructureCheck); if (!shouldConsiderForHoisting(variable)) break; noticeStructureCheck(variable, node->structureSet()); break; } case ForwardCheckStructure: case ForwardStructureTransitionWatchpoint: // We currently rely on the fact that we're the only ones who would // insert this node. RELEASE_ASSERT_NOT_REACHED(); break; case GetByOffset: case PutByOffset: case PutStructure: case AllocatePropertyStorage: case ReallocatePropertyStorage: case GetButterfly: case GetByVal: case PutByVal: case PutByValAlias: case GetArrayLength: case CheckArray: case GetIndexedPropertyStorage: case Phantom: // Don't count these uses. break; case ArrayifyToStructure: case Arrayify: if (node->arrayMode().conversion() == Array::RageConvert) { // Rage conversion changes structures. We should avoid tying to do // any kind of hoisting when rage conversion is in play. Node* child = node->child1().node(); if (child->op() != GetLocal) break; VariableAccessData* variable = child->variableAccessData(); variable->vote(VoteOther); if (!shouldConsiderForHoisting(variable)) break; noticeStructureCheck(variable, 0); } break; case SetLocal: { // Find all uses of the source of the SetLocal. If any of them are a // kind of CheckStructure, then we should notice them to ensure that // we're not hoisting a check that would contravene checks that are // already being performed. VariableAccessData* variable = node->variableAccessData(); if (!shouldConsiderForHoisting(variable)) break; Node* source = node->child1().node(); for (unsigned subIndexInBlock = 0; subIndexInBlock < block->size(); ++subIndexInBlock) { Node* subNode = block->at(subIndexInBlock); switch (subNode->op()) { case CheckStructure: { if (subNode->child1() != source) break; noticeStructureCheck(variable, subNode->structureSet()); break; } case StructureTransitionWatchpoint: { if (subNode->child1() != source) break; noticeStructureCheck(variable, subNode->structure()); break; } default: break; } } m_graph.voteChildren(node, VoteOther); break; } case GarbageValue: break; default: m_graph.voteChildren(node, VoteOther); break; } } } // Disable structure hoisting on variables that appear to mostly be used in // contexts where it doesn't make sense. for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; if (variable->voteRatio() >= Options::structureCheckVoteRatioForHoisting()) continue; HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) continue; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog( "Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable), " because the ratio is ", variable->voteRatio(), ".\n"); #endif iter->value.m_structure = 0; } // Disable structure check hoisting for variables that cross the OSR entry that // we're currently taking, and where the value currently does not have the // structure we want. for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; ASSERT(block->isReachable); if (!block->isOSRTarget) continue; if (block->bytecodeBegin != m_graph.m_osrEntryBytecodeIndex) continue; for (size_t i = 0; i < m_graph.m_mustHandleValues.size(); ++i) { int operand = m_graph.m_mustHandleValues.operandForIndex(i); Node* node = block->variablesAtHead.operand(operand); if (!node) continue; VariableAccessData* variable = node->variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) continue; if (!iter->value.m_structure) continue; JSValue value = m_graph.m_mustHandleValues[i]; if (!value || !value.isCell()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog( "Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable), " because the OSR entry value is not a cell: ", value, ".\n"); #endif iter->value.m_structure = 0; continue; } if (value.asCell()->structure() != iter->value.m_structure) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog( "Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable), " because the OSR entry value has structure ", RawPointer(value.asCell()->structure()), " and we wanted ", RawPointer(iter->value.m_structure), ".\n"); #endif iter->value.m_structure = 0; continue; } } } bool changed = false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin(); it != m_map.end(); ++it) { if (!it->value.m_structure) { dataLog( "Not hoisting checks for ", VariableAccessDataDump(m_graph, it->key), " because of heuristics.\n"); continue; } dataLog("Hoisting checks for ", VariableAccessDataDump(m_graph, it->key), "\n"); } #endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) // Place CheckStructure's at SetLocal sites. InsertionSet insertionSet(m_graph); for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { Node* node = block->at(indexInBlock); // Be careful not to use 'node' after appending to the graph. In those switch // cases where we need to append, we first carefully extract everything we need // from the node, before doing any appending. switch (node->op()) { case SetArgument: { ASSERT(!blockIndex); // Insert a GetLocal and a CheckStructure immediately following this // SetArgument, if the variable was a candidate for structure hoisting. // If the basic block previously only had the SetArgument as its // variable-at-tail, then replace it with this GetLocal. VariableAccessData* variable = node->variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) break; if (!iter->value.m_structure) break; CodeOrigin codeOrigin = node->codeOrigin; Node* getLocal = insertionSet.insertNode( indexInBlock + 1, variable->prediction(), GetLocal, codeOrigin, OpInfo(variable), Edge(node)); insertionSet.insertNode( indexInBlock + 1, SpecNone, CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->value.m_structure)), Edge(getLocal, CellUse)); if (block->variablesAtTail.operand(variable->local()) == node) block->variablesAtTail.operand(variable->local()) = getLocal; m_graph.substituteGetLocal(*block, indexInBlock, variable, getLocal); changed = true; break; } case SetLocal: { VariableAccessData* variable = node->variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) break; if (!iter->value.m_structure) break; // First insert a dead SetLocal to tell OSR that the child's value should // be dropped into this bytecode variable if the CheckStructure decides // to exit. CodeOrigin codeOrigin = node->codeOrigin; Edge child1 = node->child1(); insertionSet.insertNode( indexInBlock, SpecNone, SetLocal, codeOrigin, OpInfo(variable), child1); // Use a ForwardCheckStructure to indicate that we should exit to the // next bytecode instruction rather than reexecuting the current one. insertionSet.insertNode( indexInBlock, SpecNone, ForwardCheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->value.m_structure)), Edge(child1.node(), CellUse)); changed = true; break; } default: break; } } insertionSet.execute(block); } return changed; }
bool run() { SharedSymbolTable* symbolTable = codeBlock()->symbolTable(); // This enumerates the locals that we actually care about and packs them. So for example // if we use local 1, 3, 4, 5, 7, then we remap them: 1->0, 3->1, 4->2, 5->3, 7->4. We // treat a variable as being "used" if there exists an access to it (SetLocal, GetLocal, // Flush, PhantomLocal). BitVector usedLocals; // Collect those variables that are used from IR. bool hasGetLocalUnlinked = false; for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; for (unsigned nodeIndex = block->size(); nodeIndex--;) { Node* node = block->at(nodeIndex); switch (node->op()) { case GetLocal: case SetLocal: case Flush: case PhantomLocal: { VariableAccessData* variable = node->variableAccessData(); if (variable->local().isArgument()) break; usedLocals.set(variable->local().toLocal()); break; } case GetLocalUnlinked: { VirtualRegister operand = node->unlinkedLocal(); if (operand.isArgument()) break; usedLocals.set(operand.toLocal()); hasGetLocalUnlinked = true; break; } default: break; } } } // Ensure that captured variables and captured inline arguments are pinned down. // They should have been because of flushes, except that the flushes can be optimized // away. if (symbolTable) { for (int i = symbolTable->captureStart(); i > symbolTable->captureEnd(); i--) usedLocals.set(VirtualRegister(i).toLocal()); } if (codeBlock()->usesArguments()) { usedLocals.set(codeBlock()->argumentsRegister().toLocal()); usedLocals.set(unmodifiedArgumentsRegister(codeBlock()->argumentsRegister()).toLocal()); } if (codeBlock()->uncheckedActivationRegister().isValid()) usedLocals.set(codeBlock()->activationRegister().toLocal()); for (InlineCallFrameSet::iterator iter = m_graph.m_inlineCallFrames->begin(); !!iter; ++iter) { InlineCallFrame* inlineCallFrame = *iter; if (!inlineCallFrame->executable->usesArguments()) continue; VirtualRegister argumentsRegister = m_graph.argumentsRegisterFor(inlineCallFrame); usedLocals.set(argumentsRegister.toLocal()); usedLocals.set(unmodifiedArgumentsRegister(argumentsRegister).toLocal()); for (unsigned argument = inlineCallFrame->arguments.size(); argument-- > 1;) { usedLocals.set(VirtualRegister( virtualRegisterForArgument(argument).offset() + inlineCallFrame->stackOffset).toLocal()); } } Vector<unsigned> allocation(usedLocals.size()); m_graph.m_nextMachineLocal = 0; for (unsigned i = 0; i < usedLocals.size(); ++i) { if (!usedLocals.get(i)) { allocation[i] = UINT_MAX; continue; } allocation[i] = m_graph.m_nextMachineLocal++; } for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; if (variable->local().isArgument()) { variable->machineLocal() = variable->local(); continue; } size_t local = variable->local().toLocal(); if (local >= allocation.size()) continue; if (allocation[local] == UINT_MAX) continue; variable->machineLocal() = virtualRegisterForLocal( allocation[variable->local().toLocal()]); } if (codeBlock()->usesArguments()) { VirtualRegister argumentsRegister = virtualRegisterForLocal( allocation[codeBlock()->argumentsRegister().toLocal()]); RELEASE_ASSERT( virtualRegisterForLocal(allocation[ unmodifiedArgumentsRegister( codeBlock()->argumentsRegister()).toLocal()]) == unmodifiedArgumentsRegister(argumentsRegister)); codeBlock()->setArgumentsRegister(argumentsRegister); } if (codeBlock()->uncheckedActivationRegister().isValid()) { codeBlock()->setActivationRegister( virtualRegisterForLocal(allocation[codeBlock()->activationRegister().toLocal()])); } for (unsigned i = m_graph.m_inlineVariableData.size(); i--;) { InlineVariableData data = m_graph.m_inlineVariableData[i]; InlineCallFrame* inlineCallFrame = data.inlineCallFrame; if (inlineCallFrame->executable->usesArguments()) { inlineCallFrame->argumentsRegister = virtualRegisterForLocal( allocation[m_graph.argumentsRegisterFor(inlineCallFrame).toLocal()]); RELEASE_ASSERT( virtualRegisterForLocal(allocation[unmodifiedArgumentsRegister( m_graph.argumentsRegisterFor(inlineCallFrame)).toLocal()]) == unmodifiedArgumentsRegister(inlineCallFrame->argumentsRegister)); } for (unsigned argument = inlineCallFrame->arguments.size(); argument-- > 1;) { ArgumentPosition& position = m_graph.m_argumentPositions[ data.argumentPositionStart + argument]; VariableAccessData* variable = position.someVariable(); ValueSource source; if (!variable) source = ValueSource(SourceIsDead); else { source = ValueSource::forFlushFormat( variable->machineLocal(), variable->flushFormat()); } inlineCallFrame->arguments[argument] = source.valueRecovery(); } RELEASE_ASSERT(inlineCallFrame->isClosureCall == !!data.calleeVariable); if (inlineCallFrame->isClosureCall) { ValueSource source = ValueSource::forFlushFormat( data.calleeVariable->machineLocal(), data.calleeVariable->flushFormat()); inlineCallFrame->calleeRecovery = source.valueRecovery(); } else RELEASE_ASSERT(inlineCallFrame->calleeRecovery.isConstant()); } if (symbolTable) { if (symbolTable->captureCount()) { unsigned captureStartLocal = allocation[ VirtualRegister(codeBlock()->symbolTable()->captureStart()).toLocal()]; ASSERT(captureStartLocal != UINT_MAX); m_graph.m_machineCaptureStart = virtualRegisterForLocal(captureStartLocal).offset(); } else m_graph.m_machineCaptureStart = virtualRegisterForLocal(0).offset(); // This is an abomination. If we had captured an argument then the argument ends // up being "slow", meaning that loads of the argument go through an extra lookup // table. if (const SlowArgument* slowArguments = symbolTable->slowArguments()) { auto newSlowArguments = std::make_unique<SlowArgument[]>( symbolTable->parameterCount()); for (size_t i = symbolTable->parameterCount(); i--;) { newSlowArguments[i] = slowArguments[i]; VirtualRegister reg = VirtualRegister(slowArguments[i].index); if (reg.isLocal()) newSlowArguments[i].index = virtualRegisterForLocal(allocation[reg.toLocal()]).offset(); } m_graph.m_slowArguments = std::move(newSlowArguments); } } // Fix GetLocalUnlinked's variable references. if (hasGetLocalUnlinked) { for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; for (unsigned nodeIndex = block->size(); nodeIndex--;) { Node* node = block->at(nodeIndex); switch (node->op()) { case GetLocalUnlinked: { VirtualRegister operand = node->unlinkedLocal(); if (operand.isLocal()) operand = virtualRegisterForLocal(allocation[operand.toLocal()]); node->setUnlinkedMachineLocal(operand); break; } default: break; } } } } return true; }