From 5efcf1b9b03c291e8927d9ef69e16c970b3f73a4 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Tue, 18 Aug 2026 00:16:32 +0200 Subject: [PATCH] feat(furc/SSA): compute dominance tree --- furc/include/furc/middle/ssa.hpp | 17 ++++ furc/src/middle/ssa.cpp | 130 +++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 furc/include/furc/middle/ssa.hpp create mode 100644 furc/src/middle/ssa.cpp diff --git a/furc/include/furc/middle/ssa.hpp b/furc/include/furc/middle/ssa.hpp new file mode 100644 index 0000000..c595f24 --- /dev/null +++ b/furc/include/furc/middle/ssa.hpp @@ -0,0 +1,17 @@ +#ifndef FURC_MIDDLE_SSA_HPP +#define FURC_MIDDLE_SSA_HPP + +#include "furc/middle/ir.hpp" + +namespace furc { + +class ssa { + ssa() = delete; +public: + static void process(ir_module& mod); + static void destruct(ir_module& mod); +}; + +} // namespace furc + +#endif // FURC_MIDDLE_SSA_HPP diff --git a/furc/src/middle/ssa.cpp b/furc/src/middle/ssa.cpp new file mode 100644 index 0000000..497aac9 --- /dev/null +++ b/furc/src/middle/ssa.cpp @@ -0,0 +1,130 @@ +/** + * Sources: + * - Practical Improvements to the Construction and Deconstruction of Static Single Assignment Form: + * https://web.archive.org/web/20100607003509/http://www.cs.rice.edu/~harv/my_papers/ssa.pdf + * - A Simple, Fast Dominance Algorithm: + * https://www.researchgate.net/publication/2569680_A_Simple_Fast_Dominance_Algorithm + */ + +#include "furc/middle/ssa.hpp" + +#include "furc/middle/ir.hpp" + +#include +#include +#include +#include +#include + +namespace furc { + +namespace { + +struct block_info { + std::size_t order = 0; + + std::unordered_set preds; + std::unordered_set sucs; + std::size_t idom = 0; +}; + +void rpo_dfs(std::unordered_set& visited, + std::vector& order, + std::size_t block, + std::vector& blocks) { + visited.insert(block); + for (auto succ : blocks[block].sucs) { + if (visited.find(succ) != visited.end()) continue; + rpo_dfs(visited, order, succ, blocks); + } + order.push_back(block); +} + +void compute_rpo(std::vector& blocks, std::vector& order) { + std::unordered_set visited; + if (!blocks.empty()) rpo_dfs(visited, order, 0, blocks); + std::reverse(order.begin(), order.begin()); + for (std::size_t i = 0; i < order.size(); ++i) { + blocks[order[i]].order = i; + } +} + +std::size_t intersect(std::vector& blocks, std::size_t b1, std::size_t b2) { + std::size_t finger1 = b1; + std::size_t finger2 = b2; + while (finger1 != finger2) { + while (finger1 < finger2) + finger1 = blocks[finger1].idom; + while (finger2 < finger1) + finger2 = blocks[finger2].idom; + } + return finger1; +} + +void process_function(ir_function& func) { + std::vector blocks(func.blocks.size()); + + // 1. Compute CFG + for (std::size_t i = 0; i < func.blocks.size(); ++i) { + const auto& block = func.blocks[i]; + if (block.instructions.empty()) continue; + + const auto& termInstr = block.instructions.back(); + switch (termInstr.type) { + case ir_instruction::Branch: { + const auto& src = termInstr.sources.front(); + if (src.type != ir_operand::Block) throw std::runtime_error("invalid operand"); + blocks[src.value.block].preds.insert(i); + blocks[i].sucs.insert(src.value.block); + } break; + case ir_instruction::BranchCond: { + const auto& src = termInstr.sources.front(); + if (src.type != ir_operand::Block) throw std::runtime_error("invalid operand"); + blocks[src.value.blockPair.first].preds.insert(i); + blocks[src.value.blockPair.second].preds.insert(i); + blocks[i].preds.insert(src.value.blockPair.first); + blocks[i].preds.insert(src.value.blockPair.second); + } break; + default: break; + } + } + + // 2. Computing dominance tree + std::vector order; + order.reserve(blocks.size()); + compute_rpo(blocks, order); + + blocks[order.front()].idom = order.front(); + + bool changed = true; + while (changed) { + changed = false; + + for (std::size_t i = 1; i < order.size(); ++i) { + auto& block = blocks[order[i]]; + std::size_t newIdom = -1; + bool found = false; + for (auto pred : block.preds) { + if (blocks[pred].idom == -1) continue; + newIdom = found ? intersect(blocks, pred, newIdom) : pred; + found = true; + } + + if (block.idom != newIdom) { + block.idom = newIdom; + changed = true; + } + } + } +} + +} // namespace + +void ssa::process(ir_module& mod) { + for (auto* func : mod.functions) + process_function(*func); +} + +void ssa::destruct(ir_module& mod) {} + +} // namespace furc