feat(furc): start working on ssa optimizations

Compute dominance frontiers.

Refs: #2
This commit is contained in:
2026-06-12 12:21:52 +02:00
parent 3f6fcc56ff
commit b4fda8d7d0
3 changed files with 164 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
#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 <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace furc {
namespace front {
class ssa {
using block_map_t = std::unordered_map<furlang::ir::block_index, std::vector<furlang::ir::block_index>>;
public:
static void optimize(furlang::ir::module& mod);
private:
static void optimize(const std::unique_ptr<furlang::ir::function>& func);
static void dfs_rpo(furlang::ir::block_index block,
const block_map_t& successors,
std::unordered_set<furlang::ir::block_index>& visited,
std::vector<furlang::ir::block_index>& rpo);
};
} // namespace front
} // namespace furc
#endif // FURC_FRONT_SSA_HPP
+129
View File
@@ -0,0 +1,129 @@
#include "furc/front/ssa.hpp"
#include "furlang/ir/instruction.hpp"
#include <algorithm>
#include <cstddef>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace furc::front {
void ssa::optimize(furlang::ir::module& mod) {
for (const auto& func : mod.functions()) {
ssa::optimize(func);
}
}
void ssa::optimize(const std::unique_ptr<furlang::ir::function>& func) {
block_map_t predecessors;
block_map_t successors;
std::unordered_map<furlang::ir::block_index, furlang::ir::block_index> idoms;
for (furlang::ir::block_index i = 0; i < func->blocks().size(); ++i) {
const auto& block = func->blocks()[i];
const auto& exit = block->exit();
switch (exit->type()) {
case furlang::ir::instruction_t::Branch: {
const auto& br = reinterpret_cast<const furlang::ir::branch_instruction&>(*exit);
predecessors[br.block()].push_back(i);
successors[i].push_back(br.block());
} break;
case furlang::ir::instruction_t::BranchCond: {
const auto& br = reinterpret_cast<const furlang::ir::branch_cond_instruction&>(*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<furlang::ir::block_index> visited;
std::vector<furlang::ir::block_index> rpoOrder;
dfs_rpo(0, successors, visited, rpoOrder);
std::reverse(rpoOrder.begin(), rpoOrder.end());
std::unordered_map<furlang::ir::block_index, std::size_t> 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;
};
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<furlang::ir::block_index, std::unordered_set<furlang::ir::block_index>> df;
for (auto block : rpoOrder) {
df[block] = std::unordered_set<furlang::ir::block_index>{};
}
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];
}
}
}
}
void ssa::dfs_rpo(furlang::ir::block_index block,
const block_map_t& successors,
std::unordered_set<furlang::ir::block_index>& visited,
std::vector<furlang::ir::block_index>& 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
+3
View File
@@ -3,6 +3,7 @@
#include "furc/ast/program.hpp" #include "furc/ast/program.hpp"
#include "furc/front/ir_generator.hpp" #include "furc/front/ir_generator.hpp"
#include "furc/front/parser.hpp" #include "furc/front/parser.hpp"
#include "furc/front/ssa.hpp"
#include <iostream> #include <iostream>
@@ -34,6 +35,8 @@ int main(void) {
program->accept(generator); program->accept(generator);
auto module = std::move(generator.move_module()); auto module = std::move(generator.move_module());
furc::front::ssa::optimize(module);
std::cout << "Generated IR:\n"; std::cout << "Generated IR:\n";
for (const auto& function : module.functions()) { for (const auto& function : module.functions()) {
std::cout << function->name() << ":\n"; std::cout << function->name() << ":\n";