// Remove dead instructions by doing a traditional liveness analysis. // instructions that mutate memory, physical registers, or status flags // are considered useful. All branches are considered useful. // // Given SSA, there's a faster sparse version of this algorithm that marks // useful instructions in one pass, then transitively marks pure instructions // that define inputs to useful instructions. However it requires a mapping // from vreg numbers to the instruction that defines them, and a way to address // individual instructions. // // We could remove useless branches by computing the post-dominator tree and // RDF(b) for each block; then a branch is only useful if it controls whether // or not a useful block executes, and useless branches can be forwarded to // the nearest useful post-dominator. void removeDeadCode(Vunit& unit) { auto blocks = sortBlocks(unit); jit::vector<LiveSet> livein(unit.blocks.size()); LiveSet live(unit.next_vr); auto pass = [&](bool mutate) { bool changed = false; for (auto blockIt = blocks.end(); blockIt != blocks.begin();) { auto b = *--blockIt; auto& block = unit.blocks[b]; live.reset(); for (auto s : succs(block)) { if (!livein[s].empty()) { live |= livein[s]; } } for (auto i = block.code.end(); i != block.code.begin();) { auto& inst = *--i; auto useful = effectful(inst); visitDefs(unit, inst, [&](Vreg r) { if (r.isPhys() || live.test(r)) { useful = true; live.reset(r); } }); if (useful) { visitUses(unit, inst, [&](Vreg r) { live.set(r); }); } else if (mutate) { inst = nop{}; changed = true; } } if (mutate) { assert(live == livein[b]); } else { if (live != livein[b]) { livein[b] = live; changed = true; } } } return changed; }; // analyze until livein reaches a fixed point while (pass(false)) {} // nop-out useless instructions if (pass(true)) { for (auto b : blocks) { auto& code = unit.blocks[b].code; auto end = std::remove_if(code.begin(), code.end(), [&](Vinstr& inst) { return inst.op == Vinstr::nop; }); code.erase(end, code.end()); } printUnit(kVasmDCELevel, "after vasm-dead", unit); } }
// Remove dead instructions by doing a traditional liveness analysis. // instructions that mutate memory, physical registers, or status flags // are considered useful. All branches are considered useful. // // Given SSA, there's a faster sparse version of this algorithm that marks // useful instructions in one pass, then transitively marks pure instructions // that define inputs to useful instructions. However it requires a mapping // from vreg numbers to the instruction that defines them, and a way to address // individual instructions. // // We could remove useless branches by computing the post-dominator tree and // RDF(b) for each block; then a branch is only useful if it controls whether // or not a useful block executes, and useless branches can be forwarded to // the nearest useful post-dominator. void removeDeadCode(Vunit& unit) { Timer timer(Timer::vasm_dce); auto blocks = sortBlocks(unit); jit::vector<LiveSet> livein(unit.blocks.size()); LiveSet live(unit.next_vr); auto pass = [&](bool mutate) { bool changed = false; for (auto blockIt = blocks.end(); blockIt != blocks.begin();) { auto b = *--blockIt; auto& block = unit.blocks[b]; live.reset(); for (auto s : succs(block)) { if (!livein[s].empty()) { live |= livein[s]; } } for (auto i = block.code.end(); i != block.code.begin();) { auto& inst = *--i; auto useful = effectful(inst); visitDefs(unit, inst, [&](Vreg r) { if (r.isPhys() || live.test(r)) { useful = true; live.reset(r); } }); if (useful) { visitUses(unit, inst, [&](Vreg r) { live.set(r); }); } else if (mutate) { inst = nop{}; changed = true; } } if (mutate) { assertx(live == livein[b]); } else { if (live != livein[b]) { livein[b] = live; changed = true; } } } return changed; }; // analyze until livein reaches a fixed point while (pass(false)) {} auto const changed = pass(true); removeTrivialNops(unit); if (changed) { printUnit(kVasmDCELevel, "after vasm-dead", unit); } }