diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp index 1d51da0..c459005 100644 --- a/furc/include/furc/front/ir_generator.hpp +++ b/furc/include/furc/front/ir_generator.hpp @@ -35,6 +35,13 @@ public: void visit(const ast::binop_expression_node& node) override; void visit(const ast::var_assign_expression_node& node) override; private: + template + void push(Args&&... args) { + if (!m_currentBlock->emplace(std::forward(args)...)) { + throw std::runtime_error("block exited too soon"); + } + } + furlang::ir::block_index push_block(); private: furlang::ir::module m_module; diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp index 45fd96d..037105e 100644 --- a/furc/src/front/ir_generator.cpp +++ b/furc/src/front/ir_generator.cpp @@ -24,6 +24,7 @@ void ir_generator::visit(const ast::function_definition_node& funcDef) { for (const auto& stmt : funcDef.body()->statements) { stmt->accept(*this); } + m_currentBlock->emplace(); 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()) { returnStmt.value()->accept(*this); - m_currentBlock->emplace(ir::operand::new_reg(m_registerCounter - 1)); + push(ir::operand::new_reg(m_registerCounter - 1)); } else { - m_currentBlock->emplace(); + push(); } } void ir_generator::visit(const ast::if_statement_node& node) { node.cond()->accept(*this); ir_register cond = m_registerCounter - 1; - m_currentBlock->emplace(ir::operand::new_reg(cond), + push(ir::operand::new_reg(cond), m_currentFunction->blocks().size(), 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) { - m_currentBlock->emplace(ir::operand::new_string(std::string(*node.value())), + push(ir::operand::new_string(std::string(*node.value())), ir::operand::new_reg(m_registerCounter++)); } void ir_generator::visit(const ast::integer_literal_node& node) { - m_currentBlock->emplace(ir::operand::new_integer(*node.value()), + push(ir::operand::new_integer(*node.value()), ir::operand::new_reg(m_registerCounter++)); } void ir_generator::visit(const ast::var_read_expression_node& node) { if (auto it = m_variables.find(*node.get_name()); it != m_variables.end()) { - m_currentBlock->emplace(ir::operand::new_reg(it->second), + push(ir::operand::new_reg(it->second), ir::operand::new_reg(m_registerCounter++)); } else { throw std::runtime_error("unknown variable"); @@ -114,7 +115,7 @@ void ir_generator::visit(const ast::binop_expression_node& node) { node.rhs()->accept(*this); ir_register rhs = m_registerCounter - 1; ir_register dst = m_registerCounter++; - m_currentBlock->emplace(binary_op_instruction_t(node.type()), + push(binary_op_instruction_t(node.type()), ir::operand::new_reg(lhs), ir::operand::new_reg(rhs), ir::operand::new_reg(dst)); @@ -135,16 +136,20 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) { auto compound = node.compound(); if (compound != ast::binop_expression_node_t::None) { - m_currentBlock->emplace(binary_op_instruction_t(compound), + push(binary_op_instruction_t(compound), ir::operand::new_reg(reg), ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); } else { - m_currentBlock->emplace(ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); + push(ir::operand::new_reg(rhs), ir::operand::new_reg(reg)); } } 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(); m_currentBlock = m_currentFunction->push(); return index; diff --git a/furc/src/main.cpp b/furc/src/main.cpp index c755159..af5a313 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -39,6 +39,7 @@ int main(void) { for (const auto& instruction : block->instructions()) { std::cout << " " << *instruction << '\n'; } + std::cout << " " << *block->exit() << '\n'; } } diff --git a/furlang/include/furlang/ir/block.hpp b/furlang/include/furlang/ir/block.hpp index 2942760..6b0c8c1 100644 --- a/furlang/include/furlang/ir/block.hpp +++ b/furlang/include/furlang/ir/block.hpp @@ -18,14 +18,26 @@ public: block() = default; public: template >> - void emplace(Args&&... args) { - m_instructions.emplace_back(std::make_unique(std::forward(args)...)); + bool emplace(Args&&... args) { + if (has_exit()) return false; + auto instr = std::make_unique(std::forward(args)...); + if (is_exit_instruction(instr->type())) { + m_exit = std::move(instr); + } else { + m_instructions.emplace_back(std::move(instr)); + } + return true; } std::vector& instructions() { return m_instructions; } const std::vector& 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: std::vector m_instructions; + value_type m_exit; }; } // namespace ir diff --git a/furlang/include/furlang/ir/instruction.hpp b/furlang/include/furlang/ir/instruction.hpp index e3eb756..fed51f0 100644 --- a/furlang/include/furlang/ir/instruction.hpp +++ b/furlang/include/furlang/ir/instruction.hpp @@ -20,6 +20,15 @@ enum class instruction_t { 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 { public: instruction() = default;