Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
be386cf230
|
|||
|
a14252bd1d
|
|||
|
6b92e19b23
|
|||
|
865691df26
|
|||
|
b4fda8d7d0
|
|||
|
3f6fcc56ff
|
@@ -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
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
#include "furc/front/ssa.hpp"
|
||||||
|
|
||||||
|
#include "furlang/ir/instruction.hpp"
|
||||||
|
#include "furlang/ir/operand.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <stack>
|
||||||
|
#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::register_t, std::unordered_set<furlang::ir::block_index>> regSites;
|
||||||
|
std::unordered_map<furlang::ir::block_index, std::unordered_set<furlang::ir::register_t>> regUses;
|
||||||
|
|
||||||
|
std::unordered_map<furlang::ir::block_index, furlang::ir::block_index> idoms;
|
||||||
|
|
||||||
|
std::unordered_map<furlang::ir::register_t, std::uint32_t> regVers;
|
||||||
|
std::unordered_map<furlang::ir::register_t, std::stack<std::uint32_t>> regVerStacks;
|
||||||
|
std::unordered_map<furlang::ir::block_index, std::vector<furlang::ir::block_index>> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const furlang::ir::operand* src : instr->sources()) {
|
||||||
|
if (src->type() != furlang::ir::operand_t::Register) continue;
|
||||||
|
regUses[i].insert(src->reg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& exit = block->exit();
|
||||||
|
switch (exit->type()) {
|
||||||
|
case furlang::ir::instruction_t::Branch: {
|
||||||
|
const auto& br = dynamic_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 = dynamic_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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<furlang::ir::block_index, std::unordered_set<furlang::ir::register_t>> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [block, idom] : idoms) {
|
||||||
|
if (block != idom) domTree[idom].push_back(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<void(furlang::ir::block_index)> renameBlock = [&](furlang::ir::block_index blockIndex) -> void {
|
||||||
|
std::unordered_map<furlang::ir::register_t, std::uint32_t> 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 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<furlang::ir::phi_instruction&>(*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::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,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";
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace furlang {
|
namespace furlang {
|
||||||
namespace ir {
|
namespace ir {
|
||||||
@@ -21,6 +24,7 @@ enum class instruction_t {
|
|||||||
Branch, /**< Branch */
|
Branch, /**< Branch */
|
||||||
BranchCond, /**< Conditional branch */
|
BranchCond, /**< Conditional branch */
|
||||||
Return, /**< Return */
|
Return, /**< Return */
|
||||||
|
Phi, /**< Phi function */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,6 +70,41 @@ public:
|
|||||||
* @return The type.
|
* @return The type.
|
||||||
*/
|
*/
|
||||||
virtual instruction_t type() const = 0;
|
virtual instruction_t type() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns whether this instruction has a destination operand.
|
||||||
|
*
|
||||||
|
* @return true if this instruction has the destination operand.
|
||||||
|
*/
|
||||||
|
virtual bool has_destination() const { return false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns destination operand of this instruction.
|
||||||
|
*
|
||||||
|
* @return The destination operand.
|
||||||
|
*/
|
||||||
|
virtual operand& destination() { throw std::runtime_error("instruction type mismatch"); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns destination operand of this instruction.
|
||||||
|
*
|
||||||
|
* @return The destination operand.
|
||||||
|
*/
|
||||||
|
virtual const operand& destination() const { throw std::runtime_error("instruction type mismatch"); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a list of source operands of this instruction.
|
||||||
|
*
|
||||||
|
* @return The list of source operands.
|
||||||
|
*/
|
||||||
|
virtual std::vector<operand*> sources() { return {}; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a list of source operands of this instruction.
|
||||||
|
*
|
||||||
|
* @return The list of source operands.
|
||||||
|
*/
|
||||||
|
virtual std::vector<const operand*> sources() const { return {}; }
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Prints an instruction to an output stream.
|
* @brief Prints an instruction to an output stream.
|
||||||
@@ -158,18 +197,39 @@ public:
|
|||||||
instruction_t type() const override { return instruction_t::Assign; }
|
instruction_t type() const override { return instruction_t::Assign; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns this instruction's source.
|
* @brief Returns whether this instruction has a destination operand.
|
||||||
*
|
*
|
||||||
* @return The source.
|
* @return true
|
||||||
*/
|
*/
|
||||||
const operand& source() const { return m_source; }
|
bool has_destination() const override { return true; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns this instruction's destination.
|
* @brief Returns this instruction's destination.
|
||||||
*
|
*
|
||||||
* @return The destination.
|
* @return The destination.
|
||||||
*/
|
*/
|
||||||
const operand& destination() const { return m_destination; }
|
operand& destination() override { return m_destination; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's destination.
|
||||||
|
*
|
||||||
|
* @return The destination.
|
||||||
|
*/
|
||||||
|
const operand& destination() const override { return m_destination; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a list of this instruction's source operands.
|
||||||
|
*
|
||||||
|
* @return The list of source operands.
|
||||||
|
*/
|
||||||
|
std::vector<operand*> sources() override { return { &m_source }; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a list of this instruction's source operands.
|
||||||
|
*
|
||||||
|
* @return The list of source operands.
|
||||||
|
*/
|
||||||
|
std::vector<const operand*> sources() const override { return { &m_source }; }
|
||||||
private:
|
private:
|
||||||
operand m_source;
|
operand m_source;
|
||||||
operand m_destination;
|
operand m_destination;
|
||||||
@@ -253,6 +313,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
instruction_t type() const override { return instruction_t::BinaryOp; }
|
instruction_t type() const override { return instruction_t::BinaryOp; }
|
||||||
|
|
||||||
|
bool has_destination() const override { return true; }
|
||||||
|
|
||||||
|
operand& destination() override { return m_dst; }
|
||||||
|
|
||||||
|
const operand& destination() const override { return m_dst; }
|
||||||
|
|
||||||
|
std::vector<operand*> sources() override { return { &m_lhs, &m_rhs }; }
|
||||||
|
|
||||||
|
std::vector<const operand*> sources() const override { return { &m_lhs, &m_rhs }; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns this instruction's operation type.
|
* @brief Returns this instruction's operation type.
|
||||||
*
|
*
|
||||||
@@ -442,6 +512,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
instruction_t type() const override { return instruction_t::Return; }
|
instruction_t type() const override { return instruction_t::Return; }
|
||||||
|
|
||||||
|
std::vector<operand*> sources() override {
|
||||||
|
if (m_value.has_value()) return { &*m_value };
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const operand*> sources() const override {
|
||||||
|
if (m_value.has_value()) return { &*m_value };
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns this instruction's return value operand.
|
* @brief Returns this instruction's return value operand.
|
||||||
*
|
*
|
||||||
@@ -458,6 +538,82 @@ protected:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Phi instruction
|
||||||
|
*
|
||||||
|
* Used to implement the phi node in the SSA graph.
|
||||||
|
*/
|
||||||
|
class phi_instruction final : public instruction {
|
||||||
|
public:
|
||||||
|
phi_instruction(register_operand dst)
|
||||||
|
: m_dst(operand::new_reg(dst)) {}
|
||||||
|
|
||||||
|
~phi_instruction() override = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
phi_instruction(phi_instruction&&) noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
*/
|
||||||
|
phi_instruction& operator=(phi_instruction&&) noexcept = default;
|
||||||
|
|
||||||
|
phi_instruction(const phi_instruction&) = delete;
|
||||||
|
|
||||||
|
phi_instruction& operator=(const phi_instruction&) = delete;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's type.
|
||||||
|
*
|
||||||
|
* @return instruction_t::Phi.
|
||||||
|
*/
|
||||||
|
instruction_t type() const override { return instruction_t::Phi; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's destination register.
|
||||||
|
*
|
||||||
|
* @return The register.
|
||||||
|
*/
|
||||||
|
operand& destination() override { return m_dst; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this instruction's destination register.
|
||||||
|
*
|
||||||
|
* @return The register.
|
||||||
|
*/
|
||||||
|
const operand& destination() const override { 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.
|
||||||
|
*
|
||||||
|
* @return The labels.
|
||||||
|
*/
|
||||||
|
const std::vector<std::pair<operand, block_index>>& labels() const { return m_labels; }
|
||||||
|
private:
|
||||||
|
operand m_dst;
|
||||||
|
std::vector<std::pair<operand, block_index>> m_labels;
|
||||||
|
protected:
|
||||||
|
std::ostream& print(std::ostream& os) const override {
|
||||||
|
os << "phi " << m_dst << " =";
|
||||||
|
bool first = true;
|
||||||
|
for (const auto& pair : m_labels) {
|
||||||
|
if (!first) os << ',';
|
||||||
|
first = false;
|
||||||
|
os << ' ' << pair.second << ": " << pair.first;
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
} // namespace furlang
|
} // namespace furlang
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,33 @@ enum class operand_t {
|
|||||||
String, /**< String */
|
String, /**< String */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Alias to a register type.
|
||||||
|
*/
|
||||||
|
using register_t = std::uint32_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register operand alias.
|
* @brief Register operand alias.
|
||||||
* @see operand_t::Register
|
* @see operand_t::Register
|
||||||
*/
|
*/
|
||||||
using register_operand = std::uint32_t;
|
struct register_operand {
|
||||||
|
register_t reg;
|
||||||
|
std::uint32_t ver = 0;
|
||||||
|
|
||||||
|
register_operand(register_t reg)
|
||||||
|
: reg(reg) {}
|
||||||
|
|
||||||
|
register_operand(register_t reg, std::uint32_t ver)
|
||||||
|
: reg(reg), ver(ver) {}
|
||||||
|
|
||||||
|
operator std::uint32_t&() { return reg; }
|
||||||
|
operator const std::uint32_t&() const { return reg; }
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const register_operand& op) {
|
||||||
|
return os << op.reg << '_' << op.ver;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Variable operand alias.
|
* @brief Variable operand alias.
|
||||||
* @see operand_t::Variable
|
* @see operand_t::Variable
|
||||||
@@ -102,6 +124,19 @@ public:
|
|||||||
operand(const operand&) = delete;
|
operand(const operand&) = delete;
|
||||||
operand& operator=(const operand&) = delete;
|
operand& operator=(const operand&) = delete;
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new register operand.
|
||||||
|
*
|
||||||
|
* @param value Value of the new register operand.
|
||||||
|
* @return The register operand.
|
||||||
|
*/
|
||||||
|
static operand new_reg(register_t value) {
|
||||||
|
operand operand;
|
||||||
|
operand.m_type = operand_t::Register;
|
||||||
|
operand.m_value.reg = { value, 0 };
|
||||||
|
return operand;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new register operand.
|
* @brief Construct a new register operand.
|
||||||
*
|
*
|
||||||
@@ -163,6 +198,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
operand_t type() const { return m_type; }
|
operand_t type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this operand's register value.
|
||||||
|
*
|
||||||
|
* @return The register value.
|
||||||
|
*/
|
||||||
|
register_operand& reg() { return m_value.reg; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns this operand's register value.
|
* @brief Returns this operand's register value.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user