refactor(IR)!: change IR block structure

It now requires an exit instruction.
This commit is contained in:
2026-06-01 18:27:36 +02:00
parent de49674f81
commit 62619cd0bc
5 changed files with 45 additions and 11 deletions
+7
View File
@@ -35,6 +35,13 @@ public:
void visit(const ast::binop_expression_node& node) override; void visit(const ast::binop_expression_node& node) override;
void visit(const ast::var_assign_expression_node& node) override; void visit(const ast::var_assign_expression_node& node) override;
private: private:
template <typename T, typename... Args>
void push(Args&&... args) {
if (!m_currentBlock->emplace<T>(std::forward<Args>(args)...)) {
throw std::runtime_error("block exited too soon");
}
}
furlang::ir::block_index push_block(); furlang::ir::block_index push_block();
private: private:
furlang::ir::module m_module; furlang::ir::module m_module;
+14 -9
View File
@@ -24,6 +24,7 @@ void ir_generator::visit(const ast::function_definition_node& funcDef) {
for (const auto& stmt : funcDef.body()->statements) { for (const auto& stmt : funcDef.body()->statements) {
stmt->accept(*this); stmt->accept(*this);
} }
m_currentBlock->emplace<ir::return_instruction>();
m_module.push(std::move(m_currentFunction)); m_module.push(std::move(m_currentFunction));
} }
@@ -35,16 +36,16 @@ void ir_generator::visit(const ast::return_statement_node& returnStmt) {
if (returnStmt.value().present()) { if (returnStmt.value().present()) {
returnStmt.value()->accept(*this); returnStmt.value()->accept(*this);
m_currentBlock->emplace<ir::return_instruction>(ir::operand::new_reg(m_registerCounter - 1)); push<ir::return_instruction>(ir::operand::new_reg(m_registerCounter - 1));
} else { } else {
m_currentBlock->emplace<ir::return_instruction>(); push<ir::return_instruction>();
} }
} }
void ir_generator::visit(const ast::if_statement_node& node) { void ir_generator::visit(const ast::if_statement_node& node) {
node.cond()->accept(*this); node.cond()->accept(*this);
ir_register cond = m_registerCounter - 1; ir_register cond = m_registerCounter - 1;
m_currentBlock->emplace<ir::branch_cond_instruction>(ir::operand::new_reg(cond), push<ir::branch_cond_instruction>(ir::operand::new_reg(cond),
m_currentFunction->blocks().size(), m_currentFunction->blocks().size(),
m_currentFunction->blocks().size() + 1); m_currentFunction->blocks().size() + 1);
@@ -68,18 +69,18 @@ void ir_generator::visit(const ast::compound_statement_node& node) {
} }
void ir_generator::visit(const ast::string_literal_node& node) { void ir_generator::visit(const ast::string_literal_node& node) {
m_currentBlock->emplace<furlang::ir::assign_instruction>(ir::operand::new_string(std::string(*node.value())), push<furlang::ir::assign_instruction>(ir::operand::new_string(std::string(*node.value())),
ir::operand::new_reg(m_registerCounter++)); ir::operand::new_reg(m_registerCounter++));
} }
void ir_generator::visit(const ast::integer_literal_node& node) { void ir_generator::visit(const ast::integer_literal_node& node) {
m_currentBlock->emplace<furlang::ir::assign_instruction>(ir::operand::new_integer(*node.value()), push<furlang::ir::assign_instruction>(ir::operand::new_integer(*node.value()),
ir::operand::new_reg(m_registerCounter++)); ir::operand::new_reg(m_registerCounter++));
} }
void ir_generator::visit(const ast::var_read_expression_node& node) { void ir_generator::visit(const ast::var_read_expression_node& node) {
if (auto it = m_variables.find(*node.get_name()); it != m_variables.end()) { if (auto it = m_variables.find(*node.get_name()); it != m_variables.end()) {
m_currentBlock->emplace<furlang::ir::assign_instruction>(ir::operand::new_reg(it->second), push<furlang::ir::assign_instruction>(ir::operand::new_reg(it->second),
ir::operand::new_reg(m_registerCounter++)); ir::operand::new_reg(m_registerCounter++));
} else { } else {
throw std::runtime_error("unknown variable"); throw std::runtime_error("unknown variable");
@@ -114,7 +115,7 @@ void ir_generator::visit(const ast::binop_expression_node& node) {
node.rhs()->accept(*this); node.rhs()->accept(*this);
ir_register rhs = m_registerCounter - 1; ir_register rhs = m_registerCounter - 1;
ir_register dst = m_registerCounter++; ir_register dst = m_registerCounter++;
m_currentBlock->emplace<furlang::ir::binary_op_instruction>(binary_op_instruction_t(node.type()), push<furlang::ir::binary_op_instruction>(binary_op_instruction_t(node.type()),
ir::operand::new_reg(lhs), ir::operand::new_reg(lhs),
ir::operand::new_reg(rhs), ir::operand::new_reg(rhs),
ir::operand::new_reg(dst)); ir::operand::new_reg(dst));
@@ -135,16 +136,20 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
auto compound = node.compound(); auto compound = node.compound();
if (compound != ast::binop_expression_node_t::None) { if (compound != ast::binop_expression_node_t::None) {
m_currentBlock->emplace<ir::binary_op_instruction>(binary_op_instruction_t(compound), push<ir::binary_op_instruction>(binary_op_instruction_t(compound),
ir::operand::new_reg(reg), ir::operand::new_reg(reg),
ir::operand::new_reg(rhs), ir::operand::new_reg(rhs),
ir::operand::new_reg(reg)); ir::operand::new_reg(reg));
} else { } else {
m_currentBlock->emplace<ir::assign_instruction>(ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); push<ir::assign_instruction>(ir::operand::new_reg(rhs), ir::operand::new_reg(reg));
} }
} }
furlang::ir::block_index ir_generator::push_block() { furlang::ir::block_index ir_generator::push_block() {
if (!m_currentFunction->blocks().empty() && !m_currentFunction->blocks().back()->has_exit()) {
throw std::runtime_error(
"block " + std::to_string(m_currentFunction->blocks().size() - 1) + " is lacking an exit");
}
ir::block_index index = m_currentFunction->blocks().size(); ir::block_index index = m_currentFunction->blocks().size();
m_currentBlock = m_currentFunction->push(); m_currentBlock = m_currentFunction->push();
return index; return index;
+1
View File
@@ -39,6 +39,7 @@ int main(void) {
for (const auto& instruction : block->instructions()) { for (const auto& instruction : block->instructions()) {
std::cout << " " << *instruction << '\n'; std::cout << " " << *instruction << '\n';
} }
std::cout << " " << *block->exit() << '\n';
} }
} }
+14 -2
View File
@@ -18,14 +18,26 @@ public:
block() = default; block() = default;
public: public:
template <typename T, typename... Args, typename = std::enable_if_t<std::is_base_of_v<instruction, T>>> template <typename T, typename... Args, typename = std::enable_if_t<std::is_base_of_v<instruction, T>>>
void emplace(Args&&... args) { bool emplace(Args&&... args) {
m_instructions.emplace_back(std::make_unique<T>(std::forward<Args>(args)...)); if (has_exit()) return false;
auto instr = std::make_unique<T>(std::forward<Args>(args)...);
if (is_exit_instruction(instr->type())) {
m_exit = std::move(instr);
} else {
m_instructions.emplace_back(std::move(instr));
}
return true;
} }
std::vector<value_type>& instructions() { return m_instructions; } std::vector<value_type>& instructions() { return m_instructions; }
const std::vector<value_type>& instructions() const { return m_instructions; } const std::vector<value_type>& instructions() const { return m_instructions; }
bool has_exit() const { return m_exit != nullptr; }
value_type& exit() { return m_exit; }
const value_type& exit() const { return m_exit; }
private: private:
std::vector<value_type> m_instructions; std::vector<value_type> m_instructions;
value_type m_exit;
}; };
} // namespace ir } // namespace ir
@@ -20,6 +20,15 @@ enum class instruction_t {
Return, Return,
}; };
static inline bool is_exit_instruction(instruction_t type) {
switch (type) {
case instruction_t::Branch:
case instruction_t::BranchCond:
case instruction_t::Return: return true;
default: return false;
}
}
class instruction { class instruction {
public: public:
instruction() = default; instruction() = default;