/* * Build the CFG, then the dominator tree, then use it to validate SSA. * 1. Each src must be defined by some other instruction, and each dst must * be defined by the current instruction. * 2. Each src must be defined earlier in the same block or in a dominator. * 3. Each dst must not be previously defined. * 4. Treat tmps defined by DefConst as always defined. * 5. Each predecessor of a reachable block must be reachable (deleted * blocks must not have out-edges to reachable blocks). * 6. The entry block must not have any predecessors. */ bool checkCfg(const IRUnit& unit) { auto const blocksIds = rpoSortCfgWithIds(unit); auto const& blocks = blocksIds.blocks; jit::hash_set<const Edge*> edges; // Entry block can't have predecessors. assert(unit.entry()->numPreds() == 0); // Check valid successor/predecessor edges. for (Block* b : blocks) { auto checkEdge = [&] (const Edge* e) { assert(e->from() == b); edges.insert(e); for (auto& p : e->to()->preds()) if (&p == e) return; assert(false); // did not find edge. }; checkBlock(b); if (auto *e = b->nextEdge()) checkEdge(e); if (auto *e = b->takenEdge()) checkEdge(e); } for (Block* b : blocks) { for (DEBUG_ONLY auto const &e : b->preds()) { assert(&e == e.inst()->takenEdge() || &e == e.inst()->nextEdge()); assert(e.to() == b); } } // visit dom tree in preorder, checking all tmps auto const children = findDomChildren(unit, blocksIds); StateVector<SSATmp, bool> defined0(unit, false); forPreorderDoms(blocks.front(), children, defined0, [] (Block* block, StateVector<SSATmp, bool>& defined) { for (IRInstruction& inst : *block) { for (DEBUG_ONLY SSATmp* src : inst.srcs()) { assert(src->inst() != &inst); assert_log(src->inst()->op() == DefConst || defined[src], [&]{ return folly::format( "src '{}' in '{}' came from '{}', which is not a " "DefConst and is not defined at this use site", src->toString(), inst.toString(), src->inst()->toString()).str(); }); } for (SSATmp& dst : inst.dsts()) { assert(dst.inst() == &inst && inst.op() != DefConst); assert(!defined[dst]); defined[dst] = true; } } }); return true; }
/* * Build the CFG, then the dominator tree, then use it to validate SSA. * 1. Each src must be defined by some other instruction, and each dst must * be defined by the current instruction. * 2. Each src must be defined earlier in the same block or in a dominator. * 3. Each dst must not be previously defined. * 4. Treat tmps defined by DefConst as always defined. * 5. Each predecessor of a reachable block must be reachable (deleted * blocks must not have out-edges to reachable blocks). */ bool checkCfg(IRTrace* trace, const IRFactory& factory) { forEachTraceBlock(trace, checkBlock); // Check valid successor/predecessor edges. auto const blocks = rpoSortCfg(trace, factory); std::unordered_set<const Edge*> edges; for (Block* b : blocks) { auto checkEdge = [&] (const Edge* e) { assert(e->from() == b); edges.insert(e); for (auto& p : e->to()->preds()) if (&p == e) return; assert(false); // did not find edge. }; if (auto *e = nextEdge(b)) checkEdge(e); if (auto *e = takenEdge(b)) checkEdge(e); } for (Block* b : blocks) { for (DEBUG_ONLY auto const &e : b->preds()) { assert(&e == takenEdge(e.from()) || &e == nextEdge(e.from())); assert(e.to() == b); } } checkCatchTraces(trace, factory); // visit dom tree in preorder, checking all tmps auto const children = findDomChildren(blocks); StateVector<SSATmp, bool> defined0(&factory, false); forPreorderDoms(blocks.front(), children, defined0, [] (Block* block, StateVector<SSATmp, bool>& defined) { for (IRInstruction& inst : *block) { for (DEBUG_ONLY SSATmp* src : inst.srcs()) { assert(src->inst() != &inst); assert_log(src->inst()->op() == DefConst || defined[src], [&]{ return folly::format( "src '{}' in '{}' came from '{}', which is not a " "DefConst and is not defined at this use site", src->toString(), inst.toString(), src->inst()->toString()).str(); }); } for (SSATmp& dst : inst.dsts()) { assert(dst.inst() == &inst && inst.op() != DefConst); assert(!defined[dst]); defined[dst] = true; } } }); return true; }