feat(furc, ssa): implement phi instruction placement

Refs: #2
This commit is contained in:
2026-06-12 13:45:35 +02:00
parent b4fda8d7d0
commit 865691df26
2 changed files with 89 additions and 6 deletions
+67 -2
View File
@@ -1,9 +1,11 @@
#include "furc/front/ssa.hpp" #include "furc/front/ssa.hpp"
#include "furlang/ir/instruction.hpp" #include "furlang/ir/instruction.hpp"
#include "furlang/ir/operand.hpp"
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>
#include <memory>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@@ -20,19 +22,46 @@ void ssa::optimize(const std::unique_ptr<furlang::ir::function>& func) {
block_map_t predecessors; block_map_t predecessors;
block_map_t successors; block_map_t successors;
std::unordered_map<furlang::ir::register_operand, std::unordered_set<furlang::ir::block_index>> regSites;
std::unordered_map<furlang::ir::block_index, std::unordered_set<furlang::ir::register_operand>> regUses;
std::unordered_map<furlang::ir::block_index, furlang::ir::block_index> idoms; std::unordered_map<furlang::ir::block_index, furlang::ir::block_index> idoms;
for (furlang::ir::block_index i = 0; i < func->blocks().size(); ++i) { for (furlang::ir::block_index i = 0; i < func->blocks().size(); ++i) {
const auto& block = func->blocks()[i]; const auto& block = func->blocks()[i];
for (const auto& instr : block->instructions()) {
switch (instr->type()) {
case furlang::ir::instruction_t::Assign: {
const auto& assign = dynamic_cast<const furlang::ir::assign_instruction&>(*instr);
regSites[assign.destination().reg()].insert(i);
if (assign.source().type() == furlang::ir::operand_t::Register) {
regUses[i].insert(assign.source().reg());
}
} break;
case furlang::ir::instruction_t::BinaryOp: {
const auto& binOp = dynamic_cast<const furlang::ir::binary_op_instruction&>(*instr);
regSites[binOp.dst().reg()].insert(i);
if (binOp.lhs().type() == furlang::ir::operand_t::Register) {
regUses[i].insert(binOp.lhs().reg());
}
if (binOp.rhs().type() == furlang::ir::operand_t::Register) {
regUses[i].insert(binOp.rhs().reg());
}
} break;
default: break;
}
}
const auto& exit = block->exit(); const auto& exit = block->exit();
switch (exit->type()) { switch (exit->type()) {
case furlang::ir::instruction_t::Branch: { case furlang::ir::instruction_t::Branch: {
const auto& br = reinterpret_cast<const furlang::ir::branch_instruction&>(*exit); const auto& br = dynamic_cast<const furlang::ir::branch_instruction&>(*exit);
predecessors[br.block()].push_back(i); predecessors[br.block()].push_back(i);
successors[i].push_back(br.block()); successors[i].push_back(br.block());
} break; } break;
case furlang::ir::instruction_t::BranchCond: { case furlang::ir::instruction_t::BranchCond: {
const auto& br = reinterpret_cast<const furlang::ir::branch_cond_instruction&>(*exit); const auto& br = dynamic_cast<const furlang::ir::branch_cond_instruction&>(*exit);
predecessors[br.if_block()].push_back(i); predecessors[br.if_block()].push_back(i);
predecessors[br.else_block()].push_back(i); predecessors[br.else_block()].push_back(i);
successors[i].push_back(br.if_block()); successors[i].push_back(br.if_block());
@@ -109,6 +138,42 @@ void ssa::optimize(const std::unique_ptr<furlang::ir::function>& func) {
} }
} }
} }
std::unordered_map<furlang::ir::block_index, std::unordered_set<furlang::ir::register_operand>> phis;
for (const auto& [reg, blocks] : regSites) {
std::vector<furlang::ir::block_index> worklist(blocks.begin(), blocks.end());
std::unordered_set<furlang::ir::block_index> 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]) {
if (regUses[i].find(reg) == regUses[i].end()) continue;
auto phiInstr = std::make_unique<furlang::ir::phi_instruction>(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));
}
}
} }
void ssa::dfs_rpo(furlang::ir::block_index block, void ssa::dfs_rpo(furlang::ir::block_index block,
+21 -3
View File
@@ -468,8 +468,8 @@ protected:
*/ */
class phi_instruction final : public instruction { class phi_instruction final : public instruction {
public: public:
phi_instruction(std::vector<std::pair<operand, block_index>>&& labels) phi_instruction(register_operand dst)
: m_labels(std::move(labels)) {} : m_dst(dst) {}
~phi_instruction() override = default; ~phi_instruction() override = default;
@@ -494,6 +494,20 @@ public:
*/ */
instruction_t type() const override { return instruction_t::Phi; } instruction_t type() const override { return instruction_t::Phi; }
/**
* @brief Returns this instruction's destination register.
*
* @return The register.
*/
register_operand destination() const { return m_dst; }
/**
* @brief Returns this instruction's labels.
*
* @return The labels.
*/
std::vector<std::pair<operand, block_index>>& labels() { return m_labels; }
/** /**
* @brief Returns this instruction's labels. * @brief Returns this instruction's labels.
* *
@@ -501,11 +515,15 @@ public:
*/ */
const std::vector<std::pair<operand, block_index>>& labels() const { return m_labels; } const std::vector<std::pair<operand, block_index>>& labels() const { return m_labels; }
private: private:
register_operand m_dst;
std::vector<std::pair<operand, block_index>> m_labels; std::vector<std::pair<operand, block_index>> m_labels;
protected: protected:
std::ostream& print(std::ostream& os) const override { std::ostream& print(std::ostream& os) const override {
os << "phi"; os << "phi " << m_dst << " =";
bool first = true;
for (const auto& pair : m_labels) { for (const auto& pair : m_labels) {
if (!first) os << ',';
first = false;
os << ' ' << pair.second << ": " << pair.first; os << ' ' << pair.second << ": " << pair.first;
} }
return os; return os;