void insertIncRefs(PrcEnv& env) { auto antQ = dataflow_worklist<uint32_t, std::less<uint32_t>>(env.rpoBlocks.size()); auto avlQ = dataflow_worklist<uint32_t, std::greater<uint32_t>>(env.rpoBlocks.size()); env.states.resize(env.unit.numBlocks()); for (uint32_t i = 0; i < env.rpoBlocks.size(); i++) { auto blk = env.rpoBlocks[i]; auto& state = env.states[blk->id()]; state.rpoId = i; if (blk->numSuccs()) state.antOut.set(); if (blk->numPreds()) state.avlIn.set(); antQ.push(i); avlQ.push(i); } auto id = 0; for (auto& v : env.insertMap) { for (auto const tmp : v) { auto const blk = tmp->inst()->block(); auto& state = env.states[blk->id()]; if (!state.local.test(id)) { state.local.set(id); continue; } } id++; } using Bits = PrcState::Bits; // compute anticipated do { auto const blk = env.rpoBlocks[antQ.pop()]; auto& state = env.states[blk->id()]; state.antIn = state.antOut | state.local; state.pantIn = state.pantOut | state.local; blk->forEachPred( [&] (Block* b) { auto& s = env.states[b->id()]; auto const antOut = s.antOut & state.antIn; auto const pantOut = s.pantOut | state.pantIn; if (antOut != s.antOut || pantOut != s.pantOut) { s.antOut = antOut; s.pantOut = pantOut; antQ.push(s.rpoId); } } ); } while (!antQ.empty()); // compute available do { auto const blk = env.rpoBlocks[avlQ.pop()]; auto& state = env.states[blk->id()]; state.avlOut = state.avlIn | state.local; blk->forEachSucc( [&] (Block* b) { auto& s = env.states[b->id()]; auto const avlIn = s.avlIn & state.avlOut; if (avlIn != s.avlIn) { s.avlIn = avlIn; avlQ.push(s.rpoId); } }); } while (!avlQ.empty()); for (auto blk : env.rpoBlocks) { auto& state = env.states[blk->id()]; FTRACE(4, "InsertIncDecs: Blk(B{}) <- {}\n" "{}" " ->{}\n", blk->id(), [&] { std::string ret; blk->forEachPred([&] (Block* pred) { folly::format(&ret, " B{}", pred->id()); }); return ret; }(), show(state), [&] { std::string ret; blk->forEachSucc([&] (Block* succ) { folly::format(&ret, " B{}", succ->id()); }); return ret; }()); auto inc = state.local; for (auto inc_id = 0; inc.any(); inc >>= 1, inc_id++) { if (inc.test(0)) { auto const& tmps = env.insertMap[inc_id]; auto insert = [&] (IRInstruction* inst) { FTRACE(3, "Inserting IncRef into B{}\n", blk->id()); auto const iter = std::next(blk->iteratorTo(inst)); blk->insert(iter, env.unit.gen(IncRef, inst->bcctx(), tmps[0])); }; SSATmp* last = nullptr; // Insert an IncRef after every candidate in this block except // the last one (since we know for all but the last that its // successor is anticipated). Note that entries in tmps from // the same block are guaranteed to be in program order. for (auto const tmp : tmps) { if (tmp->inst()->block() != blk) continue; if (last) insert(last->inst()); last = tmp; } // If it's partially anticipated out, insert an inc after the // last one too. always_assert(last); if (state.pantOut.test(inc_id)) insert(last->inst()); } } auto dec = state.avlIn & ~state.pantIn; if (dec.any()) { blk->forEachPred( [&] (Block* pred) { auto& pstate = env.states[pred->id()]; dec &= pstate.pantOut; }); for (auto dec_id = 0; dec.any(); dec >>= 1, dec_id++) { if (dec.test(0)) { FTRACE(3, "Inserting DecRef into B{}\n", blk->id()); auto const tmp = env.insertMap[dec_id][0]; blk->prepend(env.unit.gen(DecRef, tmp->inst()->bcctx(), DecRefData{}, tmp)); } } } }
void optimizeStores(IRUnit& unit) { PassTracer tracer{&unit, Trace::hhir_store, "optimizeStores"}; // This isn't required for correctness, but it may allow removing stores that // otherwise we would leave alone. splitCriticalEdges(unit); auto incompleteQ = dataflow_worklist<PostOrderId>(unit.numBlocks()); /* * Global state for this pass, visible while processing any block. */ auto genv = Global { unit }; auto const& poBlockList = genv.poBlockList; if (genv.ainfo.locations.size() == 0) { FTRACE(1, "no memory accesses to possibly optimize\n"); return; } FTRACE(1, "\nLocations:\n{}\n", show(genv.ainfo)); /* * Initialize the block state structures. * * Important note: every block starts with an empty liveOut set, including * blocks that are exiting the region. When an HHIR region is exited, * there's always some instruction we can use to indicate via memory_effects * what may be read (e.g. EndCatch, RetCtrl, ReqBindJmp, etc). When we start * iterating, we'll appropriately add things to GEN based on these. */ for (auto poId = uint32_t{0}; poId < poBlockList.size(); ++poId) { genv.blockStates[poBlockList[poId]->id()].id = poId; incompleteQ.push(poId); } /* * Analyze each block to compute its transfer function. * * The blockAnalysis vector is indexed by post order id. */ auto const blockAnalysis = [&] () -> jit::vector<BlockAnalysis> { auto ret = jit::vector<BlockAnalysis>{}; ret.reserve(unit.numBlocks()); for (auto id = uint32_t{0}; id < poBlockList.size(); ++id) { ret.push_back(analyze_block(genv, poBlockList[id])); } return ret; }(); FTRACE(2, "Transfer functions:\n{}\n", [&]() -> std::string { auto ret = std::string{}; for (auto poId = uint32_t{0}; poId < poBlockList.size(); ++poId) { auto& analysis = blockAnalysis[poId]; folly::format( &ret, " B{}\n" " gen: {}\n" " kill: {}\n", poBlockList[poId]->id(), show(analysis.gen), show(analysis.kill) ); } return ret; }() ); /* * Iterate on the liveOut states until we reach a fixed point. */ FTRACE(4, "Iterating\n"); while (!incompleteQ.empty()) { auto const poId = incompleteQ.pop(); auto const blk = poBlockList[poId]; auto& state = genv.blockStates[blk->id()]; auto const transfer = blockAnalysis[poId]; assertx(state.id == poId); state.liveIn = transfer.gen | (state.liveOut & ~transfer.kill); FTRACE(4, " block B{}\n" " live out: {}\n" " gen : {}\n" " kill : {}\n" " live in : {}\n", blk->id(), show(state.liveOut), show(transfer.gen), show(transfer.kill), show(state.liveIn)); /* * Update predecessors by merging the live in state into the live out state * of each predecessor. * * If anything changes, reschedule the predecessor. */ blk->forEachPred([&] (Block* pred) { FTRACE(4, " -> {}\n", pred->id()); auto& predState = genv.blockStates[pred->id()]; auto const oldLiveOut = predState.liveOut; predState.liveOut |= state.liveIn; if (predState.liveOut != oldLiveOut) { incompleteQ.push(predState.id); } }); } /* * We've reached a fixed point. Now we can act on this information to remove * dead stores. */ FTRACE(2, "\nFixed point:\n{}\n", [&]() -> std::string { auto ret = std::string{}; for (auto& blk : poBlockList) { folly::format( &ret, " B{: <3}: {}\n", blk->id(), show(genv.blockStates[blk->id()].liveOut) ); } return ret; }() ); for (auto& block : poBlockList) { optimize_block(genv, block); } }