/* * Perform a DFS starting at block `bid', storing the post-order in * `outVec'. */ void RegionDesc::postOrderSort(RegionDesc::BlockId bid, RegionDesc::BlockIdSet& visited, RegionDesc::BlockIdVec& outVec) { if (visited.count(bid)) return; visited.insert(bid); if (auto nextRetr = nextRetrans(bid)) { postOrderSort(nextRetr.value(), visited, outVec); } for (auto succ : succs(bid)) { postOrderSort(succ, visited, outVec); } outVec.push_back(bid); }
/** * Sort the m_blocks vector in reverse post order. This enforces that * m_blocks will be a topological order in case the region is acyclic. * All region arcs are taken into account, including retranslation arcs. */ void RegionDesc::sortBlocks() { RegionDesc::BlockIdSet visited; RegionDesc::BlockIdVec reverse; postOrderSort(entry()->id(), visited, reverse); assertx(m_blocks.size() >= reverse.size()); // Remove unreachable blocks from `m_data'. for (auto it = m_blocks.begin(); it != m_blocks.end();) { auto bid = (*it)->id(); if (visited.count(bid) == 0) { it = deleteBlock(it); } else { it++; } } // Update `m_blocks' vector. m_blocks.clear(); auto size = reverse.size(); for (size_t i = 0; i < size; i++) { m_blocks.push_back(block(reverse[size - i - 1])); } }
/* * Checks if the given region is well-formed, which entails the * following properties: * * 1) The region has at least one block. * * 2) Each block in the region has a different id. * * 3) All arcs involve blocks within the region. * * 4) For each arc, the bytecode offset of the dst block must * possibly follow the execution of the src block. * * 5) Each block contains at most one successor corresponding to a * given SrcKey. * * 6) The region doesn't contain any loops, unless JitLoops is * enabled. * * 7) All blocks are reachable from the entry block. * * 8) For each block, there must be a path from the entry to it that * includes only earlier blocks in the region. * * 9) The region is topologically sorted unless loops are enabled. * * 10) The block-retranslation chains cannot have cycles. * */ bool check(const RegionDesc& region, std::string& error) { auto bad = [&](const std::string& errorMsg) { error = errorMsg; return false; }; // 1) The region has at least one block. if (region.empty()) return bad("empty region"); RegionDesc::BlockIdSet blockSet; for (auto b : region.blocks()) { auto bid = b->id(); // 2) Each block in the region has a different id. if (blockSet.count(bid)) { return bad(folly::sformat("many blocks with id {}", bid)); } blockSet.insert(bid); } for (auto b : region.blocks()) { auto bid = b->id(); SrcKey lastSk = region.block(bid)->last(); OffsetSet validSuccOffsets = lastSk.succOffsets(); OffsetSet succOffsets; for (auto succ : region.succs(bid)) { SrcKey succSk = region.block(succ)->start(); Offset succOffset = succSk.offset(); // 3) All arcs involve blocks within the region. if (blockSet.count(succ) == 0) { return bad(folly::sformat("arc with dst not in the region: {} -> {}", bid, succ)); } // Checks 4) and 5) below don't make sense for arcs corresponding // to inlined calls and returns, so skip them in such cases. // This won't be possible once task #4076399 is done. if (lastSk.func() != succSk.func()) continue; // 4) For each arc, the bytecode offset of the dst block must // possibly follow the execution of the src block. if (validSuccOffsets.count(succOffset) == 0) { return bad(folly::sformat("arc with impossible control flow: {} -> {}", bid, succ)); } // 5) Each block contains at most one successor corresponding to a // given SrcKey. if (succOffsets.count(succOffset) > 0) { return bad(folly::sformat("block {} has multiple successors with SK {}", bid, show(succSk))); } succOffsets.insert(succOffset); } for (auto pred : region.preds(bid)) { if (blockSet.count(pred) == 0) { return bad(folly::sformat("arc with src not in the region: {} -> {}", pred, bid)); } } } // 6) is checked by dfsCheck. DFSChecker dfsCheck(region); if (!dfsCheck.check(region.entry()->id())) { return bad("region is cyclic"); } // 7) All blocks are reachable from the entry (first) block. if (dfsCheck.numVisited() != blockSet.size()) { return bad("region has unreachable blocks"); } // 8) and 9) are checked below. RegionDesc::BlockIdSet visited; auto& blocks = region.blocks(); for (unsigned i = 0; i < blocks.size(); i++) { auto bid = blocks[i]->id(); unsigned nVisited = 0; for (auto pred : region.preds(bid)) { nVisited += visited.count(pred); } // 8) For each block, there must be a path from the entry to it that // includes only earlier blocks in the region. if (nVisited == 0 && i != 0) { return bad(folly::sformat("block {} appears before all its predecessors", bid)); } // 9) The region is topologically sorted unless loops are enabled. if (!RuntimeOption::EvalJitLoops && nVisited != region.preds(bid).size()) { return bad(folly::sformat("non-topological order (bid: {})", bid)); } visited.insert(bid); } // 10) The block-retranslation chains cannot have cycles. for (auto b : blocks) { auto bid = b->id(); RegionDesc::BlockIdSet chainSet; chainSet.insert(bid); while (auto next = region.nextRetrans(bid)) { auto nextId = next.value(); if (chainSet.count(nextId)) { return bad(folly::sformat("cyclic retranslation chain for block {}", bid)); } chainSet.insert(nextId); bid = nextId; } } return true; }