/* * Helper for do_analyze to initialize the states for all function entries * (i.e. each dv init and the main entry), and all of them count as places the * function could be entered, so they all must be visited at least once. * * If we're entering at a DV-init, all higher parameter locals must be * Uninit, with the possible exception of a final variadic param * (which will be an array). It is also possible that the DV-init is * reachable from within the function with these parameter locals * already initialized (although the normal php emitter can't do * this), but that case will be discovered when iterating. */ dataflow_worklist<uint32_t> prepare_incompleteQ(const Index& index, FuncAnalysis& ai, ClassAnalysis* clsAnalysis, const std::vector<Type>* knownArgs) { auto incompleteQ = dataflow_worklist<uint32_t>(ai.rpoBlocks.size()); auto const ctx = ai.ctx; auto const numParams = ctx.func->params.size(); auto const entryState = [&] { if (!is_pseudomain(ctx.func)) { return entry_state(index, ctx, clsAnalysis, knownArgs); } assert(!knownArgs && !clsAnalysis); assert(numParams == 0); return pseudomain_entry_state(ctx.func); }(); if (knownArgs) { // When we have known args, we only need to add one of the entry points to // the initial state, since we know how many arguments were passed. auto const useDvInit = [&] { if (knownArgs->size() >= numParams) return false; for (auto i = knownArgs->size(); i < numParams; ++i) { auto const dv = ctx.func->params[i].dvEntryPoint; if (dv != NoBlockId) { ai.bdata[dv].stateIn = entryState; incompleteQ.push(rpoId(ai, dv)); return true; } } return false; }(); if (!useDvInit) { ai.bdata[ctx.func->mainEntry].stateIn = entryState; incompleteQ.push(rpoId(ai, ctx.func->mainEntry)); } return incompleteQ; } for (auto paramId = uint32_t{0}; paramId < numParams; ++paramId) { auto const dv = ctx.func->params[paramId].dvEntryPoint; if (dv != NoBlockId) { ai.bdata[dv].stateIn = entryState; incompleteQ.push(rpoId(ai, dv)); for (auto locId = paramId; locId < numParams; ++locId) { ai.bdata[dv].stateIn.locals[locId] = ctx.func->params[locId].isVariadic ? TVArr : TUninit; } } } ai.bdata[ctx.func->mainEntry].stateIn = entryState; incompleteQ.push(rpoId(ai, ctx.func->mainEntry)); return incompleteQ; }
void region_prune_arcs(RegionDesc& region) { FTRACE(4, "region_prune_arcs\n"); region.sortBlocks(); auto const sortedBlocks = region.blocks(); // Maps region block ids to their RPO ids. auto blockToRPO = std::unordered_map<RegionDesc::BlockId,uint32_t>{}; auto blockInfos = std::vector<BlockInfo>(sortedBlocks.size()); auto workQ = dataflow_worklist<uint32_t>(sortedBlocks.size()); for (auto rpoID = uint32_t{0}; rpoID < sortedBlocks.size(); ++rpoID) { auto const& b = sortedBlocks[rpoID]; auto& binfo = blockInfos[rpoID]; binfo.blockID = b->id(); blockToRPO[binfo.blockID] = rpoID; } workQ.push(0); blockInfos[0].in = entry_state(region); FTRACE(4, "Iterating:\n"); do { auto const rpoID = workQ.pop(); auto& binfo = blockInfos[rpoID]; FTRACE(4, "B{}\n", binfo.blockID); binfo.out = binfo.in; apply_transfer_function( binfo.out, region.block(binfo.blockID)->postConds() ); for (auto& succ : region.succs(binfo.blockID)) { auto const succRPO = blockToRPO.find(succ); assertx(succRPO != end(blockToRPO)); auto& succInfo = blockInfos[succRPO->second]; if (preconds_may_pass(*region.block(succInfo.blockID), binfo.out)) { if (merge_into(succInfo.in, binfo.out)) { FTRACE(5, " -> {}\n", succInfo.blockID); workQ.push(succRPO->second); } } } } while (!workQ.empty()); FTRACE(2, "\nPostConds fixed point:\n{}\n", [&] () -> std::string { auto ret = std::string{}; for (auto& s : blockInfos) { folly::format(&ret, "B{}:\n{}", s.blockID, show(s.in)); } return ret; }() ); // Now remove any edge that looks like it will unconditionally fail type // predictions, and completely remove any block that can't be reached. using ArcIDs = std::pair<RegionDesc::BlockId,RegionDesc::BlockId>; auto toRemove = std::vector<ArcIDs>{}; for (auto rpoID = uint32_t{0}; rpoID < sortedBlocks.size(); ++rpoID) { auto const& binfo = blockInfos[rpoID]; for (auto& succ : region.succs(binfo.blockID)) { auto const succRPO = blockToRPO.find(succ); assertx(succRPO != end(blockToRPO)); auto const& succInfo = blockInfos[succRPO->second]; if (!binfo.in.initialized || !succInfo.in.initialized || !preconds_may_pass(*region.block(succInfo.blockID), binfo.out)) { FTRACE(2, "Pruning arc: B{} -> B{}\n", binfo.blockID, succInfo.blockID); toRemove.emplace_back(binfo.blockID, succInfo.blockID); } } for (auto& r : toRemove) region.removeArc(r.first, r.second); toRemove.clear(); } // Get rid of the completely unreachable blocks, now that any arcs to/from // them are gone. for (auto rpoID = uint32_t{0}; rpoID < sortedBlocks.size(); ++rpoID) { auto const& binfo = blockInfos[rpoID]; if (!binfo.in.initialized) { FTRACE(2, "Pruning block: B{}\n", binfo.blockID); region.deleteBlock(binfo.blockID); } } FTRACE(2, "\n"); }
void region_prune_arcs(RegionDesc& region) { FTRACE(4, "region_prune_arcs\n"); region.sortBlocks(); auto const sortedBlocks = region.blocks(); // Maps region block ids to their RPO ids. auto blockToRPO = std::unordered_map<RegionDesc::BlockId,uint32_t>{}; auto blockInfos = std::vector<BlockInfo>(sortedBlocks.size()); auto workQ = dataflow_worklist<uint32_t>(sortedBlocks.size()); for (auto rpoID = uint32_t{0}; rpoID < sortedBlocks.size(); ++rpoID) { auto const& b = sortedBlocks[rpoID]; auto& binfo = blockInfos[rpoID]; binfo.blockID = b->id(); blockToRPO[binfo.blockID] = rpoID; } workQ.push(0); blockInfos[0].in = entry_state(region); FTRACE(4, "Iterating:\n"); do { auto const rpoID = workQ.pop(); auto& binfo = blockInfos[rpoID]; FTRACE(4, "B{}\n", binfo.blockID); /* * This code currently assumes inlined functions were entirely contained * within a single profiling translation, and will need updates if we * inline bigger things in a way visible to region selection. * * Note: inlined blocks /may/ have postConditions, if they are the last * blocks from profiling translations. Currently any locations referred to * in postconditions for these blocks are for the outermost caller, so this * code handles that correctly. */ if (region.block(binfo.blockID)->inlineLevel() != 0) { assertx(region.block(binfo.blockID)->typePreConditions().empty()); } binfo.out = binfo.in; apply_transfer_function( binfo.out, region.block(binfo.blockID)->postConds() ); for (auto& succ : region.succs(binfo.blockID)) { auto const succRPO = blockToRPO.find(succ); assertx(succRPO != end(blockToRPO)); auto& succInfo = blockInfos[succRPO->second]; if (preconds_may_pass(*region.block(succInfo.blockID), binfo.out)) { if (merge_into(succInfo.in, binfo.out)) { FTRACE(5, " -> {}\n", succInfo.blockID); workQ.push(succRPO->second); } } } } while (!workQ.empty()); FTRACE(2, "\nPostConds fixed point:\n{}\n", [&] () -> std::string { auto ret = std::string{}; for (auto& s : blockInfos) { folly::format(&ret, "B{}:\n{}", s.blockID, show(s.in)); } return ret; }() ); // Now remove any edge that looks like it will unconditionally fail type // predictions, and completely remove any block that can't be reached. using ArcIDs = std::pair<RegionDesc::BlockId,RegionDesc::BlockId>; auto toRemove = std::vector<ArcIDs>{}; for (auto rpoID = uint32_t{0}; rpoID < sortedBlocks.size(); ++rpoID) { auto const& binfo = blockInfos[rpoID]; for (auto& succ : region.succs(binfo.blockID)) { auto const succRPO = blockToRPO.find(succ); assertx(succRPO != end(blockToRPO)); auto const& succInfo = blockInfos[succRPO->second]; if (!binfo.in.initialized || !succInfo.in.initialized || !preconds_may_pass(*region.block(succInfo.blockID), binfo.out)) { FTRACE(2, "Pruning arc: B{} -> B{}\n", binfo.blockID, succInfo.blockID); toRemove.emplace_back(binfo.blockID, succInfo.blockID); } } for (auto& r : toRemove) region.removeArc(r.first, r.second); toRemove.clear(); } // Get rid of the completely unreachable blocks, now that any arcs to/from // them are gone. for (auto rpoID = uint32_t{0}; rpoID < sortedBlocks.size(); ++rpoID) { auto const& binfo = blockInfos[rpoID]; if (!binfo.in.initialized) { FTRACE(2, "Pruning block: B{}\n", binfo.blockID); region.deleteBlock(binfo.blockID); } } FTRACE(2, "\n"); }