bool simplify_impl(Env& env, Vlabel b, size_t i, Simplify simplify) { auto& unit = env.unit; return vmodify(unit, b, i, [&] (Vout& v) { auto& blocks = unit.blocks; auto const nremove = simplify(v); // Update use counts for to-be-removed instructions. for (auto j = i; j < i + nremove; ++j) { visitUses(unit, blocks[b].code[j], [&] (Vreg r) { --env.use_counts[r]; }); } // Update use counts and def instructions for to-be-added instructions. for (auto const& inst : blocks[Vlabel(v)].code) { visitUses(unit, inst, [&] (Vreg r) { if (r >= env.use_counts.size()) { env.use_counts.resize(size_t{r}+1); } ++env.use_counts[r]; }); visitDefs(unit, inst, [&] (Vreg r) { if (r >= env.def_insts.size()) { env.def_insts.resize(size_t{r}+1, Vinstr::nop); } env.def_insts[r] = inst.op; }); } return nremove; }); }
// Return the effect this instruction has on the value of rsp. // this will assert if an instruction mutates rsp in an untrackable way. int rspEffect(const Vunit& unit, Vinstr& inst) { switch (inst.op) { default: visitDefs(unit, inst, [&](Vreg r) { assert(r != Vreg64(rsp)); }); return 0; case Vinstr::push: case Vinstr::pushl: case Vinstr::pushm: return -8; case Vinstr::pop: case Vinstr::popm: return 8; case Vinstr::addqi: { auto& i = inst.addqi_; if (i.d == Vreg64(rsp)) { assert(i.s1 == Vreg64(rsp)); return i.s0.l(); } return 0; } case Vinstr::subqi: { auto& i = inst.subqi_; if (i.d == Vreg64(rsp)) { assert(i.s1 == Vreg64(rsp)); return -i.s0.l(); } return 0; } } }
// 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); } }