diff --git a/furc/include/furc/front/post_process.hpp b/furc/include/furc/front/post_process.hpp new file mode 100644 index 0000000..9e4e01f --- /dev/null +++ b/furc/include/furc/front/post_process.hpp @@ -0,0 +1,39 @@ +#ifndef FURC_FRONT_POST_PROCESS_HPP +#define FURC_FRONT_POST_PROCESS_HPP + +#include "furlang/ir/module.hpp" + +#include + +namespace furc { +namespace front { + +/** + * @brief Post process pipeline. + */ +class post_process { +public: + enum stage { // NOLINT + Ssa, + DeSsa, + }; +public: + post_process() = default; + ~post_process() = default; + + post_process(post_process&&) noexcept = default; + post_process& operator=(post_process&&) noexcept = default; + post_process(const post_process&) = delete; + post_process& operator=(const post_process&) = delete; +public: + void push_stage(stage stage) { m_stages.push_back(stage); } +public: + void process(furlang::ir::mod& mod); +private: + std::vector m_stages; +}; + +} // namespace front +} // namespace furc + +#endif // FURC_FRONT_POST_PROCESS_HPP diff --git a/furc/include/furc/front/ssa.hpp b/furc/include/furc/front/ssa.hpp deleted file mode 100644 index 8d78683..0000000 --- a/furc/include/furc/front/ssa.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef FURC_FRONT_SSA_HPP -#define FURC_FRONT_SSA_HPP - -#include "furlang/ir/function.hpp" -#include "furlang/ir/instruction.hpp" -#include "furlang/ir/module.hpp" - -#include -#include -#include -#include - -namespace furc { -namespace front { - -class ssa { - using block_map_t = std::unordered_map>; -public: - static void optimize(furlang::ir::mod& mod); -private: - static void optimize(const std::unique_ptr& func); - - static void de_ssa(const std::unique_ptr& func); - - static void constant_propagation(const std::unique_ptr& func); - - static void dead_code_elimination(const std::unique_ptr& func); - - static void copy_propagation(const std::unique_ptr& func); - - static void dfs_rpo(furlang::ir::block_index block, - const block_map_t& successors, - std::unordered_set& visited, - std::vector& rpo); -}; - -} // namespace front -} // namespace furc - -#endif // FURC_FRONT_SSA_HPP diff --git a/furc/src/front/post_process.cpp b/furc/src/front/post_process.cpp new file mode 100644 index 0000000..9fb1908 --- /dev/null +++ b/furc/src/front/post_process.cpp @@ -0,0 +1,321 @@ +#include "furc/front/post_process.hpp" + +#include "furlang/ir/function.hpp" +#include "furlang/ir/instruction.hpp" +#include "furlang/ir/operand.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace furc::front { + +using block_idx = furlang::ir::block_index; +using reigster_t = furlang::ir::register_t; +using reigster_op = furlang::ir::register_operand; + +static constexpr block_idx INVALID_BLOCK = std::numeric_limits::max(); + +struct block_info { + std::size_t rpoIndex{ 0 }; + + std::vector predecessors; + std::vector successors; + + block_idx idom = INVALID_BLOCK; + std::vector doms; + std::unordered_set domFrontiers; +}; + +struct register_info { + std::unordered_set defSites; + std::stack renameStack; + std::uint32_t nextVersion{ 0 }; +}; + +struct function_context { + explicit function_context(furlang::ir::function* function) + : function(function) { + build_cfg(); + compute_rpo(); + } + + void build_cfg() { + for (block_idx idx = 0; idx < function->blocks().size(); ++idx) { + const auto& block = function->blocks()[idx]; + + for (const auto& instr : block->instructions()) { + for (const auto& operand : instr->sources()) { + if (operand->type() != furlang::ir::operand_t::Register) continue; + auto reg = operand->reg(); + if (registers[reg].defSites.find(idx) != registers[reg].defSites.end()) continue; + globalRegisters.insert(reg); + } + } + + for (const auto& instr : block->instructions()) { + if (!instr->has_destination() || instr->destination().type() != furlang::ir::operand_t::Register) + continue; + auto reg = instr->destination().reg(); + registers[reg].defSites.insert(idx); + } + + for (const auto& operand : block->exit()->sources()) { + if (operand->type() != furlang::ir::operand_t::Register) continue; + auto reg = operand->reg(); + if (registers[reg].defSites.find(idx) != registers[reg].defSites.end()) continue; + globalRegisters.insert(reg); + } + + const auto& exit = block->exit(); + switch (exit->type()) { + case furlang::ir::instruction_t::Branch: { + const auto& br = dynamic_cast(*exit); + blocks[br.block()].predecessors.push_back(idx); + blocks[idx].successors.push_back(br.block()); + } break; + case furlang::ir::instruction_t::BranchCond: { + const auto& br = dynamic_cast(*exit); + blocks[br.if_block()].predecessors.push_back(idx); + blocks[br.else_block()].predecessors.push_back(idx); + blocks[idx].successors.push_back(br.if_block()); + blocks[idx].successors.push_back(br.else_block()); + } break; + default: break; + } + } + } + + void compute_rpo() { + std::unordered_set visited; + + auto dfs = [&](auto& self, block_idx block) -> void { + visited.insert(block); + for (auto succ : blocks[block].successors) { + if (visited.find(succ) != visited.end()) continue; + self(self, succ); + } + rpoOrder.push_back(block); + }; + + if (!function->blocks().empty()) dfs(dfs, 0); + + std::reverse(rpoOrder.begin(), rpoOrder.end()); + for (std::size_t i = 0; i < rpoOrder.size(); ++i) { + blocks[rpoOrder[i]].rpoIndex = i; + } + } + + void compute_dominance() { + if (rpoOrder.empty()) return; + + const block_idx entry = rpoOrder.front(); + blocks[entry].idom = entry; + + bool changed = true; + while (changed) { + changed = false; + + for (block_idx idx : rpoOrder) { + if (idx == entry) continue; + + block_idx newIdom = INVALID_BLOCK; + bool found = false; + for (auto pred : blocks[idx].predecessors) { + if (blocks[pred].idom == INVALID_BLOCK) continue; + if (found) { + newIdom = intersect(pred, newIdom); + } else { + newIdom = pred; + found = true; + } + } + + if (blocks[idx].idom != newIdom) { + blocks[idx].idom = newIdom; + changed = true; + } + } + } + + for (auto idx : rpoOrder) { + if (idx == entry) continue; + const block_idx parent = blocks[idx].idom; + if (parent != INVALID_BLOCK) blocks[parent].doms.push_back(idx); + } + + for (auto idx : rpoOrder) { + if (blocks[idx].predecessors.size() < 2) continue; + for (auto cur : blocks[idx].predecessors) { + while (cur != blocks[idx].idom) { + blocks[cur].domFrontiers.insert(idx); + cur = blocks[cur].idom; + } + } + } + } + + furlang::ir::function* function; + std::vector rpoOrder; + std::unordered_map blocks; + std::unordered_map registers; + std::unordered_set globalRegisters; +private: + block_idx intersect(block_idx block1, block_idx block2) { + while (block1 != block2) { + while (blocks[block1].rpoIndex > blocks[block2].rpoIndex) + block1 = blocks[block1].idom; + while (blocks[block2].rpoIndex > blocks[block1].rpoIndex) + block2 = blocks[block2].idom; + } + return block1; + } +}; + +static void ssa_stage_rename_block(function_context& ctx, + block_idx idx, + std::unordered_map& regVers, + std::unordered_map>& regVerStacks) { + std::unordered_map pushed; + + const auto& block = ctx.function->blocks()[idx]; + auto it = block->instructions().begin(); + for (; it != block->instructions().end(); ++it) { + auto& instr = *it; + if (instr->type() != furlang::ir::instruction_t::Phi) break; + + const register_t orig = instr->destination().reg(); + const std::uint32_t newVer = regVers[orig]++; + + instr->destination().reg().ver = newVer; + regVerStacks[orig].push(newVer); + ++pushed[orig]; + } + + for (; it != block->instructions().end(); ++it) { + auto& instr = *it; + for (auto& operand : instr->sources()) { + if (operand->type() != furlang::ir::operand_t::Register) continue; + + const register_t orig = operand->reg(); + if (regVerStacks[orig].empty()) continue; + operand->reg().ver = regVerStacks[orig].top(); + } + + if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { + const register_t orig = instr->destination().reg(); + const std::uint32_t newVer = regVers[orig]++; + + instr->destination().reg().ver = newVer; + regVerStacks[orig].push(newVer); + ++pushed[orig]; + } + } + + for (auto& operand : block->exit()->sources()) { + if (operand->type() != furlang::ir::operand_t::Register) continue; + + const auto orig = operand->reg(); + if (regVerStacks[orig].empty()) continue; + operand->reg().ver = regVerStacks[orig].top(); + } + + for (auto succIdx : ctx.blocks[idx].successors) { + const auto& succ = ctx.function->blocks()[succIdx]; + for (auto& instr : succ->instructions()) { + if (instr->type() != furlang::ir::instruction_t::Phi) break; + + auto& phi = dynamic_cast(*instr); + for (auto& pair : phi.labels()) { + if (pair.second != idx) continue; + + auto orig = pair.first.reg(); + if (auto it = regVerStacks.find(orig); it != regVerStacks.end()) + pair.first.reg().ver = it->second.top(); + } + } + } + + for (const auto& child : ctx.blocks[idx].doms) { + ssa_stage_rename_block(ctx, child, regVers, regVerStacks); + } + + for (const auto& [reg, count] : pushed) { + for (std::size_t i = 0; i < count; ++i) + regVerStacks[reg].pop(); + } +} + +static void ssa_stage(function_context& ctx) { + ctx.compute_dominance(); + + std::vector worklist; + for (const auto& [reg, info] : ctx.registers) { + if (info.defSites.size() < 2 || ctx.globalRegisters.find(reg) == ctx.globalRegisters.end()) continue; + + worklist.clear(); + worklist.insert(worklist.end(), info.defSites.begin(), info.defSites.end()); + + std::unordered_set added; + while (!worklist.empty()) { + const auto idx = worklist.back(); + worklist.pop_back(); + for (auto frontier : ctx.blocks[idx].domFrontiers) { + if (added.find(frontier) != added.end()) continue; + added.insert(frontier); + + const auto& target = ctx.function->blocks()[frontier]; + const auto& preds = ctx.blocks[frontier].predecessors; + + auto instr = std::make_unique(reg); + for (auto pred : preds) { + instr->labels().emplace_back(furlang::ir::operand::new_reg(reg), pred); + } + target->instructions().emplace(target->instructions().begin(), std::move(instr)); + + if (info.defSites.find(frontier) == info.defSites.end()) worklist.push_back(frontier); + } + } + } + + std::unordered_map regVers; + std::unordered_map> regVerStacks; + ssa_stage_rename_block(ctx, ctx.rpoOrder.front(), regVers, regVerStacks); +} + +static void dessa_stage(function_context& ctx) { + for (block_idx idx = 0; idx < ctx.function->blocks().size(); ++idx) { + const auto& block = ctx.function->blocks()[idx]; + auto& instrs = block->instructions(); + for (auto it = instrs.begin(); it != instrs.end() && (*it)->type() == furlang::ir::instruction_t::Phi; + it = instrs.erase(it)) { + auto& phi = dynamic_cast(**it); + auto dstReg = phi.destination().reg(); + for (auto& [srcOp, label] : phi.labels()) { + ctx.function->blocks()[label]->instructions().push_back( + std::make_unique(furlang::ir::operand::new_reg(srcOp.reg()), + furlang::ir::operand::new_reg(dstReg))); + } + } + } +} + +void post_process::process(furlang::ir::mod& mod) { + for (const auto& func : mod.functions()) { + if (!func || func->blocks().empty()) continue; + function_context ctx{ func.get() }; + + for (const auto& stage : m_stages) { + switch (stage) { + case Ssa: ssa_stage(ctx); break; + case DeSsa: dessa_stage(ctx); break; + } + } + } +} + +} // namespace furc::front diff --git a/furc/src/front/ssa.cpp b/furc/src/front/ssa.cpp deleted file mode 100644 index 5c413ba..0000000 --- a/furc/src/front/ssa.cpp +++ /dev/null @@ -1,640 +0,0 @@ -#include "furc/front/ssa.hpp" - -#include "furlang/ir/instruction.hpp" -#include "furlang/ir/operand.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace furc::front { - -void ssa::optimize(furlang::ir::mod& mod) { - for (const auto& func : mod.functions()) { - ssa::optimize(func); - ssa::constant_propagation(func); - ssa::dead_code_elimination(func); - ssa::copy_propagation(func); - ssa::dead_code_elimination(func); - ssa::de_ssa(func); - } -} - -void ssa::optimize(const std::unique_ptr& func) { - block_map_t predecessors; - block_map_t successors; - - std::unordered_map> regSites; - - std::unordered_map idoms; - - std::unordered_map regVers; - std::unordered_map> regVerStacks; - std::unordered_map> domTree; - - for (furlang::ir::block_index i = 0; i < func->blocks().size(); ++i) { - const auto& block = func->blocks()[i]; - - for (const auto& instr : block->instructions()) { - if (instr->has_destination()) { - if (instr->destination().type() == furlang::ir::operand_t::Register) { - regSites[instr->destination().reg()].insert(i); - } - } - } - - const auto& exit = block->exit(); - switch (exit->type()) { - case furlang::ir::instruction_t::Branch: { - const auto& br = dynamic_cast(*exit); - predecessors[br.block()].push_back(i); - successors[i].push_back(br.block()); - } break; - case furlang::ir::instruction_t::BranchCond: { - const auto& br = dynamic_cast(*exit); - predecessors[br.if_block()].push_back(i); - predecessors[br.else_block()].push_back(i); - successors[i].push_back(br.if_block()); - successors[i].push_back(br.else_block()); - } break; - default: break; - } - } - - std::unordered_set globalRegs; - for (furlang::ir::block_index i = 0; i < func->blocks().size(); ++i) { - const auto& block = func->blocks()[i]; - for (const auto& instr : block->instructions()) { - for (const auto& operand : instr->sources()) { - if (operand->type() == furlang::ir::operand_t::Register) { - auto reg = operand->reg(); - if (regSites[reg].find(i) == regSites[reg].end()) { - globalRegs.insert(reg); - } - } - } - } - for (const auto& operand : block->exit()->sources()) { - if (operand->type() == furlang::ir::operand_t::Register) { - auto reg = operand->reg(); - if (regSites[reg].find(i) == regSites[reg].end()) { - globalRegs.insert(reg); - } - } - } - } - - std::unordered_set visited; - std::vector rpoOrder; - dfs_rpo(0, successors, visited, rpoOrder); - std::reverse(rpoOrder.begin(), rpoOrder.end()); - - std::unordered_map rpoIndex; - for (std::size_t i = 0; i < rpoOrder.size(); ++i) { - rpoIndex[rpoOrder[i]] = i; - } - - auto intersect = [&](furlang::ir::block_index block1, furlang::ir::block_index block2) { - while (block1 != block2) { - while (rpoIndex[block1] > rpoIndex[block2]) { - block1 = idoms[block1]; - } - while (rpoIndex[block2] > rpoIndex[block1]) { - block2 = idoms[block2]; - } - } - return block1; - }; - - if (rpoOrder.empty()) return; - auto entry = rpoOrder.front(); - idoms[entry] = entry; - - bool changed = true; - while (changed) { - changed = false; - - for (std::size_t i = 1; i < rpoOrder.size(); ++i) { - auto block = rpoOrder[i]; - - furlang::ir::block_index newIdom = 0; - bool found = false; - for (auto pred : predecessors[block]) { - if (idoms.find(pred) == idoms.end()) continue; - if (!found) { - newIdom = pred; - found = true; - } else { - newIdom = intersect(pred, newIdom); - } - } - - if (idoms.find(block) == idoms.end() || idoms[block] != newIdom) { - idoms[block] = newIdom; - changed = true; - } - } - } - - std::unordered_map> df; - for (auto block : rpoOrder) { - df[block] = std::unordered_set{}; - } - - for (auto block : rpoOrder) { - if (predecessors[block].size() < 2) continue; - - for (auto pred : predecessors[block]) { - auto runner = pred; - while (runner != idoms[block]) { - df[runner].insert(block); - runner = idoms[runner]; - } - } - } - - std::unordered_map> phis; - - for (const auto& [reg, blocks] : regSites) { - if (globalRegs.find(reg) == globalRegs.end()) continue; - - std::vector worklist(blocks.begin(), blocks.end()); - std::unordered_set added; - while (!worklist.empty()) { - auto block = worklist.back(); - worklist.pop_back(); - for (auto frontier : df[block]) { - if (added.find(frontier) != added.end()) continue; - phis[frontier].insert(reg); - added.insert(frontier); - if (blocks.find(frontier) == blocks.end()) { - worklist.push_back(frontier); - } - } - } - } - - for (furlang::ir::block_index i = 0; i < func->blocks().size(); ++i) { - if (phis.find(i) == phis.end()) continue; - const auto& block = func->blocks()[i]; - const auto& preds = predecessors[i]; - - for (auto reg : phis[i]) { - auto phiInstr = std::make_unique(reg); - for (auto pred : preds) { - phiInstr->labels().emplace_back(furlang::ir::operand::new_reg(reg), pred); - } - block->instructions().emplace(block->instructions().begin(), std::move(phiInstr)); - } - } - - for (const auto& [block, idom] : idoms) { - if (block != idom) domTree[idom].push_back(block); - } - - std::function renameBlock = [&](furlang::ir::block_index blockIndex) -> void { - std::unordered_map pushed; - - const auto& block = func->blocks()[blockIndex]; - for (auto& instr : block->instructions()) { - if (instr->type() != furlang::ir::instruction_t::Phi) continue; - auto orig = instr->destination().reg(); - std::uint32_t newVer = regVers[orig]++; - instr->destination().reg().ver = newVer; - regVerStacks[orig].push(newVer); - ++pushed[orig]; - } - - for (auto& instr : block->instructions()) { - if (instr->type() == furlang::ir::instruction_t::Phi) continue; - - for (auto& operand : instr->sources()) { - if (operand->type() != furlang::ir::operand_t::Register) continue; - auto orig = operand->reg(); - if (!regVerStacks[orig].empty()) { - operand->reg().ver = regVerStacks[orig].top(); - } - } - - if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { - auto orig = instr->destination().reg(); - std::uint32_t newVer = regVers[orig]++; - instr->destination().reg().ver = newVer; - regVerStacks[orig].push(newVer); - ++pushed[orig]; - } - } - - for (auto& operand : block->exit()->sources()) { - if (operand->type() != furlang::ir::operand_t::Register) continue; - auto orig = operand->reg(); - if (!regVerStacks[orig].empty()) { - operand->reg().ver = regVerStacks[orig].top(); - } - } - - for (auto succIndex : successors[blockIndex]) { - const auto& succ = func->blocks()[succIndex]; - for (auto& instr : succ->instructions()) { - if (instr->type() != furlang::ir::instruction_t::Phi) break; - auto& phi = dynamic_cast(*instr); - for (auto& [op, bl] : phi.labels()) { - if (bl != blockIndex) continue; - if (regVerStacks[op.reg()].empty()) continue; - op.reg().ver = regVerStacks[op.reg()].top(); - } - } - } - - for (const auto& child : domTree[blockIndex]) { - renameBlock(child); - } - - for (const auto& [reg, count] : pushed) { - for (std::uint32_t i = 0; i < count; ++i) { - regVerStacks[reg].pop(); - } - } - }; - - renameBlock(entry); -} - -void ssa::de_ssa(const std::unique_ptr& func) { - using namespace furlang::ir; - - std::unordered_map>> copies; - for (block_index blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - auto& instrs = block->instructions(); - for (auto it = instrs.begin(); it != instrs.end();) { - if ((*it)->type() != furlang::ir::instruction_t::Phi) { - ++it; - continue; - } - auto& phi = dynamic_cast(**it); - auto dstReg = phi.destination().reg(); - for (auto& [srcOp, label] : phi.labels()) { - copies[label].emplace_back( - std::make_unique(std::move(srcOp), operand::new_reg(dstReg))); - } - it = instrs.erase(it); - } - } - - for (auto& [blockIdx, cpys] : copies) { - if (blockIdx >= func->blocks().size()) continue; - const auto& block = func->blocks()[blockIdx]; - auto& instrs = block->instructions(); - for (auto& copy : cpys) { - instrs.push_back(std::move(copy)); - } - } -} - -enum class lattice_t { - Top, - Constant, - Bottom, -}; - -struct lattice { - lattice_t type = lattice_t::Top; - std::uint64_t value = 0; - - bool operator==(const lattice& rhs) const { - if (type != rhs.type) return false; - return type != lattice_t::Constant || value == rhs.value; - } -}; - -void ssa::constant_propagation(const std::unique_ptr& func) { - using block_idx = furlang::ir::block_index; - using reg_t = furlang::ir::register_operand; - using reg2_t = furlang::ir::register_t; - - std::unordered_map latVals; - std::unordered_map> edges; - std::unordered_set executableBlocks; - std::set> executedEdges; - - std::queue> cfgWorklist; - std::queue ssaWorklist; - - std::unordered_map blockMap; - - auto getOperandLattice = [&](const furlang::ir::operand& op) -> lattice { - if (op.type() == furlang::ir::operand_t::Integer) { - lattice lat; - lat.type = lattice_t::Constant; - lat.value = op.integer(); - return lat; - } - if (op.type() == furlang::ir::operand_t::Register) { - auto reg = op.reg(); - if (latVals.find(reg) == latVals.end()) return { lattice_t::Top }; - return latVals[reg]; - } - return { lattice_t::Bottom }; - }; - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - auto& instrs = block->instructions(); - for (auto it = instrs.begin(); it != instrs.end(); ++it) { - const auto& instr = *it; - blockMap[instr.get()] = blockIdx; - for (const auto& op : instr->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - edges[op->reg()].push_back(instr.get()); - } - } - - blockMap[block->exit().get()] = blockIdx; - for (const auto& op : block->exit()->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - edges[op->reg()].push_back(block->exit().get()); - } - } - - cfgWorklist.push({ 0, 0 }); - while (!cfgWorklist.empty() || !ssaWorklist.empty()) { - if (!cfgWorklist.empty()) { - auto edge = cfgWorklist.front(); - cfgWorklist.pop(); - block_idx from = edge.first; - block_idx to = edge.second; - - if (executedEdges.count(edge) != 0) continue; - executedEdges.insert(edge); - - bool firstVisit = (executableBlocks.find(to) == executableBlocks.end()); - executableBlocks.insert(to); - - const auto& block = func->blocks()[to]; - if (firstVisit) { - for (auto& instr : block->instructions()) { - ssaWorklist.push(instr.get()); - } - ssaWorklist.push(block->exit().get()); - } else { - for (auto& instr : block->instructions()) { - if (instr->type() != furlang::ir::instruction_t::Phi) break; - ssaWorklist.push(instr.get()); - } - } - } - - if (!ssaWorklist.empty()) { - auto* instr = ssaWorklist.front(); - ssaWorklist.pop(); - - block_idx blockIdx = blockMap[instr]; - if (executableBlocks.find(blockIdx) == executableBlocks.end()) continue; - - lattice newLat = { lattice_t::Top }; - - switch (instr->type()) { - case furlang::ir::instruction_t::Phi: { - auto& phi = dynamic_cast(*instr); - for (const auto& [op, label] : phi.labels()) { - if (executedEdges.count({ label, blockIdx }) == 0) continue; - lattice opLat = getOperandLattice(op); - if (opLat.type == lattice_t::Bottom) newLat.type = lattice_t::Bottom; - if (opLat.type == lattice_t::Constant) { - if (newLat.type == lattice_t::Top) { - newLat = opLat; - } else if (newLat.type == lattice_t::Constant && newLat.value != opLat.value) { - newLat.type = lattice_t::Bottom; - } - } - } - } break; - case furlang::ir::instruction_t::Assign: { - newLat = getOperandLattice(*instr->sources().front()); - } break; - case furlang::ir::instruction_t::BinaryOp: { - lattice lhs = getOperandLattice(*instr->sources()[0]); - lattice rhs = getOperandLattice(*instr->sources()[1]); - - if (lhs.type == lattice_t::Bottom || rhs.type == lattice_t::Bottom) { - newLat.type = lattice_t::Bottom; - } else if (lhs.type == lattice_t::Constant && rhs.type == lattice_t::Constant) { - newLat.type = lattice_t::Constant; - switch (dynamic_cast(*instr).op_type()) { - case furlang::ir::binary_op_instruction_t::Add: newLat.value = lhs.value + rhs.value; break; - case furlang::ir::binary_op_instruction_t::Sub: newLat.value = lhs.value - rhs.value; break; - case furlang::ir::binary_op_instruction_t::Mul: newLat.value = lhs.value * rhs.value; break; - case furlang::ir::binary_op_instruction_t::Div: newLat.value = lhs.value / rhs.value; break; - case furlang::ir::binary_op_instruction_t::Mod: newLat.value = lhs.value % rhs.value; break; - case furlang::ir::binary_op_instruction_t::Eq: - newLat.value = (lhs.value == rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::NotEq: - newLat.value = (lhs.value != rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::LessThan: - newLat.value = (lhs.value < rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::GreaterThan: - newLat.value = (lhs.value > rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::LessEq: - newLat.value = (lhs.value <= rhs.value) ? 1 : 0; - break; - case furlang::ir::binary_op_instruction_t::GreaterEq: - newLat.value = (lhs.value >= rhs.value) ? 1 : 0; - break; - } - } - } break; - default: break; - } - - if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { - auto dst = instr->destination().reg(); - if (!(latVals[dst] == newLat)) { - latVals[dst] = newLat; - for (auto* uInstr : edges[dst]) - ssaWorklist.push(uInstr); - } - } - - if (instr == func->blocks()[blockIdx]->exit().get()) { - auto* exit = func->blocks()[blockIdx]->exit().get(); - if (exit->type() == furlang::ir::instruction_t::Branch) { - auto& br = dynamic_cast(*exit); - cfgWorklist.push({ blockIdx, br.block() }); - } else if (exit->type() == furlang::ir::instruction_t::BranchCond) { - auto& br = dynamic_cast(*exit); - lattice cond = getOperandLattice(*exit->sources()[0]); - - if (cond.type == lattice_t::Constant) { - if (cond.value != 0) - cfgWorklist.push({ blockIdx, br.if_block() }); - else - cfgWorklist.push({ blockIdx, br.else_block() }); - } else { - cfgWorklist.push({ blockIdx, br.if_block() }); - cfgWorklist.push({ blockIdx, br.else_block() }); - } - } - } - } - } - - for (block_idx i = 0; i < func->blocks().size(); ++i) { - if (executableBlocks.find(i) == executableBlocks.end()) { - func->blocks()[i]->instructions().clear(); - continue; - } - - const auto& block = func->blocks()[i]; - - for (auto& instr : block->instructions()) { - for (auto& op : instr->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - auto reg = op->reg(); - if (latVals[reg].type != lattice_t::Constant) continue; - *op = furlang::ir::operand::new_integer(latVals[reg].value); - } - } - - auto* exit = block->exit().get(); - if (exit->type() != furlang::ir::instruction_t::BranchCond) continue; - auto& br = dynamic_cast(*exit); - lattice cond = getOperandLattice(*exit->sources()[0]); - if (cond.type != lattice_t::Constant) continue; - block_idx target = (cond.value != 0) ? br.if_block() : br.else_block(); - block->exit() = std::make_unique(target); - } -} - -void ssa::dead_code_elimination(const std::unique_ptr& func) { - using block_idx = furlang::ir::block_index; - using reg_t = furlang::ir::register_operand; - - std::unordered_map defMap; - std::unordered_set alive; - std::queue worklist; - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - - for (auto& instr : block->instructions()) { - if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) { - defMap[instr->destination().reg()] = instr.get(); - } - } - - auto* exit = block->exit().get(); - alive.insert(exit); - worklist.push(exit); - } - - while (!worklist.empty()) { - const auto* instr = worklist.front(); - worklist.pop(); - - for (const auto& op : instr->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - auto src = op->reg(); - - if (defMap.find(src) == defMap.end()) continue; - auto* defInstr = defMap[src]; - if (alive.insert(defInstr).second) { - worklist.push(defInstr); - } - } - } - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - auto& instrs = block->instructions(); - - for (auto it = instrs.begin(); it != instrs.end();) { - if (alive.find(it->get()) == alive.end()) { - it = instrs.erase(it); - } else { - ++it; - } - } - } -} - -void ssa::copy_propagation(const std::unique_ptr& func) { - using block_idx = furlang::ir::block_index; - using reg_t = furlang::ir::register_operand; - - std::unordered_map aliasMap; - - std::function findRep = [&](const reg_t& reg) -> reg_t { - auto it = aliasMap.find(reg); - if (it == aliasMap.end()) return reg; - reg_t act = findRep(it->second); - aliasMap[reg] = act; - return act; - }; - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - for (auto& instr : block->instructions()) { - if (instr->type() != furlang::ir::instruction_t::Assign) continue; - auto& srcOp = *instr->sources().front(); - if (srcOp.type() != furlang::ir::operand_t::Register || - instr->destination().type() != furlang::ir::operand_t::Register) - continue; - reg_t dstReg = instr->destination().reg(); - reg_t srcReg = srcOp.reg(); - reg_t repSrc = findRep(srcReg); - reg_t repDst = findRep(dstReg); - if (repSrc == repDst) continue; - aliasMap[repDst] = repSrc; - } - } - - for (block_idx blockIdx = 0; blockIdx < func->blocks().size(); ++blockIdx) { - const auto& block = func->blocks()[blockIdx]; - for (auto& instr : block->instructions()) { - for (auto& op : instr->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - op->reg() = findRep(op->reg()); - } - if (instr->type() != furlang::ir::instruction_t::Phi) continue; - auto& phi = dynamic_cast(*instr); - for (auto& [op, label] : phi.labels()) { - if (op.type() != furlang::ir::operand_t::Register) continue; - op.reg() = findRep(op.reg()); - } - } - - for (auto& op : block->exit()->sources()) { - if (op->type() != furlang::ir::operand_t::Register) continue; - op->reg() = findRep(op->reg()); - } - } -} - -void ssa::dfs_rpo(furlang::ir::block_index block, - const block_map_t& successors, - std::unordered_set& visited, - std::vector& rpo) { - visited.insert(block); - if (auto it = successors.find(block); it != successors.end()) { - for (auto successor : it->second) { - if (visited.find(successor) == visited.end()) { - dfs_rpo(successor, successors, visited, rpo); - } - } - } - rpo.push_back(block); -} - -} // namespace furc::front diff --git a/furc/src/main.cpp b/furc/src/main.cpp index 8de070f..c1a5759 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -3,7 +3,7 @@ #include "furc/ast/program.hpp" #include "furc/front/ir_generator.hpp" #include "furc/front/parser.hpp" -#include "furc/front/ssa.hpp" +#include "furc/front/post_process.hpp" #include "furlang/arena.hpp" #include @@ -32,11 +32,15 @@ int main(void) { const auto& program = *programResult; program->accept(generator); - auto module = std::move(generator.move_module()); - furc::front::ssa::optimize(module); + auto mod = std::move(generator.move_module()); + + furc::front::post_process postProcess; + postProcess.push_stage(furc::front::post_process::Ssa); + postProcess.push_stage(furc::front::post_process::DeSsa); + postProcess.process(mod); std::cout << "Generated IR:\n"; - for (const auto& function : module.functions()) { + for (const auto& function : mod.functions()) { std::cout << function->name() << ":\n"; furlang::ir::block_index blockIndex = 0; for (const auto& block : function->blocks()) {