void optimizeJmps(Vunit& unit) { auto isEmpty = [&](Vlabel b, Vinstr::Opcode op) { auto& code = unit.blocks[b].code; return code.size() == 1 && op == code[0].op; }; bool changed = false; bool ever_changed = false; jit::vector<int> npreds(unit.blocks.size(), 0); do { if (changed) { fill(npreds.begin(), npreds.end(), 0); } changed = false; PostorderWalker{unit}.dfs([&](Vlabel b) { for (auto s : succs(unit.blocks[b])) { npreds[s]++; } }); // give roots an extra predecessor to prevent cloning them. for (auto b : unit.roots) { npreds[b]++; } PostorderWalker{unit}.dfs([&](Vlabel b) { auto& block = unit.blocks[b]; auto& code = block.code; assert(!code.empty()); if (code.back().op == Vinstr::jcc) { auto ss = succs(block); if (ss[0] == ss[1]) { // both edges have same target, change to jmp code.back() = jmp{ss[0]}; changed = true; } } if (code.back().op == Vinstr::jmp) { auto& s = code.back().jmp_.target; if (isEmpty(s, Vinstr::jmp)) { // skip over s s = unit.blocks[s].code.back().jmp_.target; changed = true; } else if (npreds[s] == 1 || isEmpty(s, Vinstr::jcc)) { // overwrite jmp with copy of s auto& code2 = unit.blocks[s].code; code.pop_back(); code.insert(code.end(), code2.begin(), code2.end()); changed = true; } } else { for (auto& s : succs(block)) { if (isEmpty(s, Vinstr::jmp)) { // skip over s s = unit.blocks[s].code.back().jmp_.target; changed = true; } } } }); ever_changed |= changed; } while (changed); if (ever_changed) { printUnit(kVasmJumpsLevel, "after vasm-jumps", unit); } }
void optimizeJmps(Vunit& unit) { auto isEmpty = [&](Vlabel b, Vinstr::Opcode op) { auto& code = unit.blocks[b].code; return code.size() == 1 && op == code[0].op; }; bool changed = false; bool ever_changed = false; // The number of incoming edges from (reachable) predecessors for each block. // It is maintained as an upper bound of the actual value during the // transformation. jit::vector<int> npreds(unit.blocks.size(), 0); do { if (changed) { std::fill(begin(npreds), end(npreds), 0); } changed = false; PostorderWalker{unit} .dfs([&](Vlabel b) { for (auto s : succs(unit.blocks[b])) { npreds[s]++; } }); // give entry an extra predecessor to prevent cloning it. npreds[unit.entry]++; PostorderWalker{unit} .dfs([&](Vlabel b) { auto& block = unit.blocks[b]; auto& code = block.code; assertx(!code.empty()); if (code.back().op == Vinstr::jcc) { auto ss = succs(block); if (ss[0] == ss[1]) { // both edges have same target, change to jmp code.back() = jmp{ss[0]}; --npreds[ss[0]]; changed = true; } else { auto jcc_i = code.back().jcc_; if (isEmpty(jcc_i.targets[0], Vinstr::fallback)) { jcc_i = jcc{ccNegate(jcc_i.cc), jcc_i.sf, {jcc_i.targets[1], jcc_i.targets[0]}}; } if (isEmpty(jcc_i.targets[1], Vinstr::fallback)) { // replace jcc with fallbackcc and jmp const auto& fb_i = unit.blocks[jcc_i.targets[1]].code[0].fallback_; const auto t0 = jcc_i.targets[0]; const auto jcc_origin = code.back().origin; code.pop_back(); code.emplace_back( fallbackcc{jcc_i.cc, jcc_i.sf, fb_i.dest, fb_i.trflags}); code.back().origin = jcc_origin; code.emplace_back(jmp{t0}); code.back().origin = jcc_origin; changed = true; } } } for (auto& s : succs(block)) { if (isEmpty(s, Vinstr::jmp)) { // skip over s --npreds[s]; s = unit.blocks[s].code.back().jmp_.target; ++npreds[s]; changed = true; } } if (code.back().op == Vinstr::jmp) { auto s = code.back().jmp_.target; if (npreds[s] == 1 || isEmpty(s, Vinstr::jcc)) { // overwrite jmp with copy of s auto& code2 = unit.blocks[s].code; code.pop_back(); code.insert(code.end(), code2.begin(), code2.end()); if (--npreds[s]) { for (auto ss : succs(block)) { ++npreds[ss]; } } changed = true; } } }); ever_changed |= changed; } while (changed); if (ever_changed) { printUnit(kVasmJumpsLevel, "after vasm-jumps", unit); } }