void LinearScan::allocRegsToTrace() { ExitTraceMap etm; numberInstructions(m_blocks); if (HPHP::Trace::moduleEnabled(HPHP::Trace::hhir, 5)) { std::stringstream s; s << "RPO: "; for (auto& b : m_blocks) { s << folly::format("{}{} ", b->isMain() ? "M" : "E", b->id()); } s << "\n"; HPHP::Trace::traceRelease("%s\n", s.str().c_str()); } BlockList::iterator it = m_blocks.begin(); while (it != m_blocks.end()) { allocRegsOneTrace(it, etm); } for (it = m_blocks.begin(); it != m_blocks.end();) { if ((*it)->isMain()) { ++it; continue; } allocRegsOneTrace(it, etm); } }
// This function attempts to find a pre-coloring hint from two // different sources: If tmp comes from a DefLabel, it will scan up to // the SSATmps providing values to incoming Jmp_s to look for a // hint. If tmp is consumed by a Jmp_, look for other incoming Jmp_s // to its destination and see if any of them have already been given a // register. If all of these fail, let normal register allocation // proceed unhinted. RegNumber LinearScan::getJmpPreColor(SSATmp* tmp, uint32_t regIndex, bool isReload) { IRInstruction* srcInst = tmp->inst(); const JmpList& jmps = m_jmps[tmp]; if (isReload && (srcInst->op() == DefLabel || !jmps.empty())) { // If we're precoloring a Reload of a temp that we'd normally find // a hint for, just return the register allocated to the spilled // temp. auto reg = m_allocInfo[tmp].reg(regIndex); assert(reg != reg::noreg); return reg; } if (srcInst->op() == DefLabel) { // Figure out which dst of the label is tmp for (unsigned i = 0, n = srcInst->numDsts(); i < n; ++i) { if (srcInst->dst(i) == tmp) { auto reg = findLabelSrcReg(m_allocInfo, srcInst, i, regIndex); // Until we handle loops, it's a bug to try and allocate a // register to a DefLabel's dest before all of its incoming // Jmp_s have had their srcs allocated, unless the incoming // block is unreachable. const DEBUG_ONLY bool unreachable = std::find(m_blocks.begin(), m_blocks.end(), srcInst->block()) == m_blocks.end(); always_assert(reg != reg::noreg || unreachable); return reg; } } not_reached(); } // If srcInst wasn't a label, check if tmp is used by any Jmp_ // instructions. If it is, trace to the Jmp_'s label and use the // same procedure as above. for (unsigned ji = 0, jn = jmps.size(); ji < jn; ++ji) { IRInstruction* jmp = jmps[ji]; IRInstruction* label = jmp->taken()->front(); // Figure out which src of the Jmp_ is tmp for (unsigned si = 0, sn = jmp->numSrcs(); si < sn; ++si) { SSATmp* src = jmp->src(si); if (tmp == src) { // For now, a DefLabel should never have a register assigned // to it before any of its incoming Jmp_ instructions. always_assert(m_allocInfo[label->dst(si)].reg(regIndex) == reg::noreg); auto reg = findLabelSrcReg(m_allocInfo, label, si, regIndex); if (reg != reg::noreg) return reg; } } } return reg::noreg; }
void LinearScan::collectInfo(BlockList::iterator it, IRTrace* trace) { m_natives.clear(); m_uses.reset(); // TODO(#2536764): serious time sink while (it != m_blocks.end()) { Block* block = *it++; bool offTrace = block->trace() != trace; if (offTrace) { if (!trace->isMain()) return; int lastId = block->trace()->data(); for (IRInstruction& inst : *block) { for (auto* src : inst.srcs()) { if (lastId > m_uses[src].lastUse) { m_uses[src].lastUse = lastId; } } } } else { for (IRInstruction& inst : *block) { for (auto* src : inst.srcs()) { m_uses[src].lastUse = m_linear[inst]; } if (inst.isNative()) m_natives.push_back(&inst); } IRInstruction* jmp = block->back(); if (jmp->op() == Jmp_ && jmp->numSrcs() != 0) { for (SSATmp* src : jmp->srcs()) { m_jmps[src].push_back(jmp); } } } } }
typename MemoryManager<AllocatorType>::BlockList::iterator MemoryManager<AllocatorType>::findBlock(BlockList& blockList, Byte* address, size_t index) { typename BlockList::iterator it{blockList.begin()}; const size_t blockSize{1UL << index}; // Loop while the address is not in the block pointed by it while((address < *it or address >= *it + blockSize) and it != blockList.end()) ++it; return it; }
// // Compute the generate and kill sets for each basic block in the given // function. The generate and kill functions are overriden by the subclass. // void DataFlowPass::initStates(const BlockList& blocks, BlockStates& states) { for (BlockList::const_iterator FI = blocks.begin(), FE = blocks.end(); FI != FE; ++FI) { const BasicBlock& block = *FI; BlockState state; initState(block, state); states.insert(BlockStatePair(&block, state)); } }
void reflowTypes(Block* const changed, const BlockList& blocks) { assert(isRPOSorted(blocks)); auto it = rpoIteratorTo(blocks, changed); assert(it != blocks.end()); for (; it != blocks.end(); ++it) { FTRACE(5, "reflowTypes: visiting block {}\n", (*it)->id()); for (auto& inst : **it) visitInstruction(&inst); } }
void write_bc_file(const BlockList &blockList, const std::string &file_name) { FILE *fp = fopen(file_name.c_str(), "wb"); if (!fp) { abort_("[write_bc_file] File %s cound not be opened for writing", file_name.c_str()); } cout << "Writing " << file_name << " ..." << endl; for (BlockList::const_iterator it=blockList.begin(); it!=blockList.end(); ++it) { fwrite(&it->front(), 1, it->size(), fp); } fclose(fp); }
BlockList rpoSortCfg(const IRUnit& unit) { BlockList blocks; blocks.reserve(unit.numBlocks()); postorderWalk(unit, [&](Block* block) { blocks.push_back(block); }); std::reverse(blocks.begin(), blocks.end()); assert(blocks.size() <= unit.numBlocks()); return blocks; }
void cloneToBlock(const BlockList& rpoBlocks, IRFactory& irFactory, Block::iterator const first, Block::iterator const last, Block* const target) { assert(isRPOSorted(rpoBlocks)); StateVector<SSATmp,SSATmp*> rewriteMap(irFactory, nullptr); auto rewriteSources = [&] (IRInstruction* inst) { for (int i = 0; i < inst->numSrcs(); ++i) { if (auto newTmp = rewriteMap[inst->src(i)]) { FTRACE(5, " rewrite: {} -> {}\n", inst->src(i)->toString(), newTmp->toString()); inst->setSrc(i, newTmp); } } }; auto targetIt = target->skipHeader(); for (auto it = first; it != last; ++it) { assert(!it->isControlFlow()); FTRACE(5, "cloneToBlock({}): {}\n", target->id(), it->toString()); auto const newInst = irFactory.cloneInstruction(&*it); if (auto const numDests = newInst->numDsts()) { for (int i = 0; i < numDests; ++i) { FTRACE(5, " add rewrite: {} -> {}\n", it->dst(i)->toString(), newInst->dst(i)->toString()); rewriteMap[it->dst(i)] = newInst->dst(i); } } target->insert(targetIt, newInst); targetIt = ++target->iteratorTo(newInst); } auto it = rpoIteratorTo(rpoBlocks, target); for (; it != rpoBlocks.end(); ++it) { FTRACE(5, "cloneToBlock: rewriting block {}\n", (*it)->id()); for (auto& inst : **it) { FTRACE(5, " rewriting {}\n", inst.toString()); rewriteSources(&inst); } } }
// // Called after the pass is complete. // Show the results of the pass for each program point b/t blocks. // void DataFlowPass::display(const BlockList& blocks, BlockStates& states) { for (BlockList::const_iterator I = blocks.begin(), IE = blocks.end(); I != IE; ++I) { const BasicBlock* block = &(*I); BlockState& state = states[block]; if (I == blocks.begin()) { DataFlowUtil::print(state.in); cout << endl; } block->dump(); DataFlowUtil::print(state.out); cout << endl; } cout << endl; }
/* * Find the immediate dominator of each block using Cooper, Harvey, and * Kennedy's "A Simple, Fast Dominance Algorithm", returned as a vector * of postorder ids, indexed by postorder id. */ IdomVector findDominators(const BlockList& blocks) { assert(isRPOSorted(blocks)); // Calculate immediate dominators with the iterative two-finger algorithm. // When it terminates, idom[post-id] will contain the post-id of the // immediate dominator of each block. idom[start] will be -1. This is // the general algorithm but it will only loop twice for loop-free graphs. auto const num_blocks = blocks.size(); IdomVector idom(num_blocks, -1); auto start = blocks.begin(); int start_id = (*start)->postId(); idom[start_id] = start_id; start++; for (bool changed = true; changed; ) { changed = false; // for each block after start, in reverse postorder for (auto it = start; it != blocks.end(); it++) { Block* block = *it; int b = block->postId(); // new_idom = any already-processed predecessor auto edge_it = block->preds().begin(); int new_idom = edge_it->from()->postId(); while (idom[new_idom] == -1) new_idom = (++edge_it)->from()->postId(); // for all other already-processed predecessors p of b for (auto& edge : block->preds()) { auto p = edge.from()->postId(); if (p != new_idom && idom[p] != -1) { // find earliest common predecessor of p and new_idom // (higher postIds are earlier in flow and in dom-tree). int b1 = p, b2 = new_idom; do { while (b1 < b2) b1 = idom[b1]; while (b2 < b1) b2 = idom[b2]; } while (b1 != b2); new_idom = b1; } } if (idom[b] != new_idom) { idom[b] = new_idom; changed = true; } } } idom[start_id] = -1; // start has no idom. return idom; }
BlockList rpoSortCfg(IRTrace* trace, const IRFactory& factory) { assert(trace->isMain()); BlockList blocks; blocks.reserve(factory.numBlocks()); unsigned next_id = 0; postorderWalk( [&](Block* block) { block->setPostId(next_id++); blocks.push_back(block); }, factory.numBlocks(), trace->front() ); std::reverse(blocks.begin(), blocks.end()); assert(blocks.size() <= factory.numBlocks()); assert(next_id <= factory.numBlocks()); return blocks; }
/*removes the temp blocks in the current scope, then delete the scope's TempBlockStack */ void BlockManager::leave_scope() { BlockList* temps = temp_block_list_stack_.back(); BlockList::iterator it; for (it = temps->begin(); it != temps->end(); ++it) { BlockId &block_id = *it; int array_id = block_id.array_id(); // Cached delete for distributed/served arrays. // Regular delete for temp blocks. if (sip_tables_.is_distributed(array_id) || sip_tables_.is_served(array_id)) cached_delete_block(*it); else delete_block(*it); // delete_block(*it); } temp_block_list_stack_.pop_back(); delete temps; }
/* * Find the immediate dominator of each block using Cooper, Harvey, and * Kennedy's "A Simple, Fast Dominance Algorithm", returned as a vector * of Block*, indexed by block. IdomVector[b] == nullptr if b has no * dominator. This is the case for the entry block and any blocks not * reachable from the entry block. */ IdomVector findDominators(const IRUnit& unit, const BlockList& blocks, const BlockIDs& rpoIDs) { // Calculate immediate dominators with the iterative two-finger algorithm. // When it terminates, idom[post-id] will contain the post-id of the // immediate dominator of each block. idom[start] will be -1. This is // the general algorithm but it will only loop twice for loop-free graphs. IdomVector idom(unit, nullptr); auto start = blocks.begin(); auto entry = *start; idom[entry] = entry; start++; for (bool changed = true; changed; ) { changed = false; // for each block after start, in reverse postorder for (auto it = start; it != blocks.end(); it++) { Block* block = *it; // p1 = any already-processed predecessor auto predIter = block->preds().begin(); auto predEnd = block->preds().end(); auto p1 = predIter->from(); while (!idom[p1]) p1 = (++predIter)->from(); // for all other already-processed predecessors p2 of block for (++predIter; predIter != predEnd; ++predIter) { auto p2 = predIter->from(); if (p2 == p1 || !idom[p2]) continue; // find earliest common predecessor of p1 and p2 // (lower RPO ids are earlier in flow and in dom-tree). do { while (rpoIDs[p1] < rpoIDs[p2]) p2 = idom[p2]; while (rpoIDs[p2] < rpoIDs[p1]) p1 = idom[p1]; } while (p1 != p2); } if (idom[block] != p1) { idom[block] = p1; changed = true; } } } idom[entry] = nullptr; // entry has no dominator. return idom; }
void reflowTypes(Block* const changed, const BlockList& blocks) { assert(isRPOSorted(blocks)); auto retypeDst = [&] (IRInstruction* inst, int num) { auto ssa = inst->dst(num); /* * The type of a tmp defined by DefLabel is the union of the * types of the tmps at each incoming Jmp. */ if (inst->op() == DefLabel) { Type type = Type::Bottom; inst->block()->forEachSrc(num, [&](IRInstruction*, SSATmp* tmp) { type = Type::unionOf(type, tmp->type()); }); ssa->setType(type); return; } ssa->setType(outputType(inst, num)); }; auto visit = [&] (IRInstruction* inst) { for (int i = 0; i < inst->numDsts(); ++i) { auto const ssa = inst->dst(i); auto const oldType = ssa->type(); retypeDst(inst, i); if (!ssa->type().equals(oldType)) { FTRACE(5, "reflowTypes: retyped {} in {}\n", oldType.toString(), inst->toString()); } } }; auto it = rpoIteratorTo(blocks, changed); assert(it != blocks.end()); for (; it != blocks.end(); ++it) { FTRACE(5, "reflowTypes: visiting block {}\n", (*it)->id()); for (auto& inst : **it) visit(&inst); } }
/** Perform simple tests to remove redundant blocks. */ void optimize(BlockList & blocks) { int changes = 1; while (changes > 0) { changes = 0; BlockIter it = blocks.begin(); while (it != blocks.end()) { // Negative start indicates block is unreachable. if ((*it)->start < 0) { it = blocks.erase(it); ++changes; } // A block can be removed if it has no statements // and does not change locking status. else if ((*it)->stmts.size() == 0 && !(*it)->unlock) { int start = (*it)->start; // cerr << "\nRemoving " << start << endl; Node closure = (*it)->closure; int transfer = (*it)->transfer; for (BlockIter ib = blocks.begin(); ib != blocks.end(); ++ib) { if ((*ib)->start == transfer && closure !=0) (*ib)->closure = closure; if ((*ib)->transfer == start) (*ib)->transfer = transfer; if ((*ib)->altTransfer == start) (*ib)->altTransfer = transfer; } it = blocks.erase(it); ++changes; } else ++it; } // Build a set of all labels in use set<int> labels; for (BlockIter it = blocks.begin(); it != blocks.end(); ++it) { labels.insert((*it)->transfer); labels.insert((*it)->altTransfer); // Include addresses of option nodes for (ListIter is = (*it)->stmts.begin(); is != (*it)->stmts.end(); ++is) { NodeKind k = (*is)->kind(); if (k == OPTION_NODE || k == RECEIVE_OPTION_NODE || k == SEND_OPTION_NODE) labels.insert((*it)->start); } } // cerr << "Labels: "; // for (set<int>::iterator it = labels.begin(); it != labels.end(); ++it) // cerr << ' ' << *it; // cerr << endl; it = blocks.begin(); while (it != blocks.end()) { if ((*it)->closure == 0 && labels.find((*it)->start) == labels.end()) { it = blocks.erase(it); ++changes; } else ++it; } } }
void LinearScan::allocRegsOneTrace(BlockList::iterator& blockIt, ExitTraceMap& etm) { auto const trace = (*blockIt)->trace(); collectInfo(blockIt, trace); computePreColoringHint(); auto v = etm.find(*blockIt); if (v != etm.end()) { assert(!trace->isMain()); v->second.restore(this); } else { assert(blockIt == m_blocks.begin() && trace->isMain()); initFreeList(); } // First, visit every instruction, allocating registers as we go, // and inserting Reload instructions where necessary. bool isMain = trace->isMain(); size_t sz = m_slots.size(); while (blockIt != m_blocks.end()) { Block* block = *blockIt; if (block->trace() != trace) { if (!isMain) { break; } else { ++blockIt; continue; } } FTRACE(5, "Block{}: {} ({})\n", trace->isMain() ? "" : " (exit trace)", (*blockIt)->id(), (*blockIt)->postId()); // clear remembered reloads that don't dominate this block for (SlotInfo& slot : m_slots) { if (SSATmp* reload = slot.latestReload) { if (!dominates(reload->inst()->block(), block, m_idoms)) { slot.latestReload = nullptr; } } } for (auto it = block->begin(), end = block->end(); it != end; ++it) { allocRegToInstruction(it); dumpIR<IRInstruction, kExtraLevel>(&*it, "allocated to instruction "); } if (isMain) { assert(block->trace()->isMain()); if (block->taken() && !block->taken()->trace()->isMain()) { etm[block->taken()].save(this); } } ++blockIt; } // Now that we have visited all instructions on this trace, // and inserted Reloads for SSATmps which needed to be spilled, // we can go back and insert the spills. // On the main trace, insert the spill right after the instruction // that generated the value (without traversing everything else). // On exit traces, if the instruction that generated the value // is on the main trace, insert the spill at the start of the trace, // otherwise, after the instruction that generated the value size_t begin = sz; size_t end = m_slots.size(); while (begin < end) { SlotInfo& slot = m_slots[begin++]; IRInstruction* spill = slot.spillTmp->inst(); IRInstruction* inst = spill->src(0)->inst(); Block* block = inst->block(); if (!isMain && block->trace()->isMain()) { // We're on an exit trace, but the def is on the // main trace, so put it at the start of this trace if (spill->block()) { // its already been inserted in another exit trace assert(!spill->block()->trace()->isMain()); spill = m_unit.cloneInstruction(spill); } trace->front()->prepend(spill); } else if (inst->isBlockEnd()) { block->next()->prepend(spill); } else { auto pos = block->iteratorTo(inst); block->insert(++pos, spill); } } }