From 646d185d40e7d54ca89437d9046aa0ced6e6cd47 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Sun, 31 May 2026 16:15:53 +0200 Subject: [PATCH] Implement basic IR generator Signed-off-by: CHatingPython --- furc/include/furc/front/ir_generator.hpp | 44 ++++++++++ furc/src/front/ir_generator.cpp | 96 ++++++++++++++++++++++ furc/src/main.cpp | 28 ++++++- furlang/include/furlang/ir/block.hpp | 4 +- furlang/include/furlang/ir/function.hpp | 35 ++++++++ furlang/include/furlang/ir/instruction.hpp | 80 ++++++++++++++++++ furlang/include/furlang/ir/module.hpp | 32 ++++++++ furlang/include/furlang/ir/operand.hpp | 11 +++ 8 files changed, 324 insertions(+), 6 deletions(-) create mode 100644 furc/include/furc/front/ir_generator.hpp create mode 100644 furc/src/front/ir_generator.cpp create mode 100644 furlang/include/furlang/ir/function.hpp create mode 100644 furlang/include/furlang/ir/module.hpp diff --git a/furc/include/furc/front/ir_generator.hpp b/furc/include/furc/front/ir_generator.hpp new file mode 100644 index 0000000..ac959c5 --- /dev/null +++ b/furc/include/furc/front/ir_generator.hpp @@ -0,0 +1,44 @@ +#ifndef FURC_FRONT_IR_GENERATOR_HPP +#define FURC_FRONT_IR_GENERATOR_HPP + +#include "furc/ast/fwd.hpp" +#include "furc/ast/visitor.hpp" +#include "furlang/ir/module.hpp" + +#include + +namespace furc { +namespace front { + +class ir_generator final : public ast::visitor { +public: + ir_generator() = default; + ~ir_generator() override = default; + + ir_generator(ir_generator&&) = default; + ir_generator& operator=(ir_generator&&) = default; + ir_generator(const ir_generator&) = default; + ir_generator& operator=(const ir_generator&) = default; +public: + furlang::ir::module&& move_module() { return std::move(m_module); } +public: + void visit_function_definition_node(const ast::function_definition_node& funcDef) override; + void visit_return_statement_node(const ast::return_statement_node& returnStmt) override; + void visit_string_literal_node(const ast::string_literal_node& node) override; + void visit_integer_literal_node(const ast::integer_literal_node& node) override; + void visit_var_read_expression_node(const ast::var_read_expression_node& node) override; + void visit_unaryop_expression_node(const ast::unaryop_expression_node& node) override; + void visit_binop_expression_node(const ast::binop_expression_node& node) override; + void visit_var_assign_expression_node(const ast::var_assign_expression_node& node) override; +private: + furlang::ir::module m_module; + std::shared_ptr m_currentBlock; + std::uint32_t m_registerCounter = 0; + + std::unordered_map m_variableMap; +}; + +} // namespace front +} // namespace furc + +#endif // FURC_FRONT_IR_GENERATOR_HPP \ No newline at end of file diff --git a/furc/src/front/ir_generator.cpp b/furc/src/front/ir_generator.cpp new file mode 100644 index 0000000..e470a34 --- /dev/null +++ b/furc/src/front/ir_generator.cpp @@ -0,0 +1,96 @@ +#include "furc/front/ir_generator.hpp" + +#include "furc/ast/declaration.hpp" // IWYU pragma: keep +#include "furc/ast/expression.hpp" // IWYU pragma: keep +#include "furc/ast/literal.hpp" // IWYU pragma: keep +#include "furc/ast/statement.hpp" // IWYU pragma: keep + +#include + +namespace furc::front { + +namespace { +namespace ir = furlang::ir; +} + +void ir_generator::visit_function_definition_node(const ast::function_definition_node& funcDef) { + auto func = std::make_unique(std::string(funcDef.name()->string)); + + m_currentBlock = func->push(); + if (funcDef.body().has_error()) { + std::cerr << funcDef.body().error() << '\n'; + return; + } + for (const auto& stmt : funcDef.body()->statements) { + stmt->accept(*this); + } + + m_module.push(std::move(func)); +} + +void ir_generator::visit_return_statement_node(const ast::return_statement_node& returnStmt) { + if (returnStmt.value().has_error()) { + std::cerr << returnStmt.value() << '\n'; + } + + if (returnStmt.value().present()) { + returnStmt.value()->accept(*this); + m_currentBlock->emplace(ir::operand::new_reg(m_registerCounter - 1)); + } else { + m_currentBlock->emplace(); + } +} + +void ir_generator::visit_string_literal_node(const ast::string_literal_node& node) { + m_currentBlock->emplace(ir::operand::new_string(std::string(*node.value())), + ir::operand::new_reg(m_registerCounter++)); +} + +void ir_generator::visit_integer_literal_node(const ast::integer_literal_node& node) { + m_currentBlock->emplace(ir::operand::new_integer(*node.value()), + ir::operand::new_reg(m_registerCounter++)); +} + +void ir_generator::visit_var_read_expression_node(const ast::var_read_expression_node& node) { + throw std::runtime_error("unimplemented"); +} + +void ir_generator::visit_unaryop_expression_node(const ast::unaryop_expression_node& node) { + throw std::runtime_error("unimplemented"); +} + +static inline furlang::ir::binary_op_instruction_t binary_op_instruction_t(ast::binop_expression_node_t type) { + switch (type) { + case ast::binop_expression_node_t::Add: return furlang::ir::binary_op_instruction_t::Add; + case ast::binop_expression_node_t::Sub: return furlang::ir::binary_op_instruction_t::Sub; + case ast::binop_expression_node_t::Mul: return furlang::ir::binary_op_instruction_t::Mul; + case ast::binop_expression_node_t::Div: return furlang::ir::binary_op_instruction_t::Div; + case ast::binop_expression_node_t::Mod: return furlang::ir::binary_op_instruction_t::Mod; + case ast::binop_expression_node_t::Equal: return furlang::ir::binary_op_instruction_t::Eq; + case ast::binop_expression_node_t::NotEqual: return furlang::ir::binary_op_instruction_t::NotEq; + case ast::binop_expression_node_t::LessThan: return furlang::ir::binary_op_instruction_t::LessThan; + case ast::binop_expression_node_t::GreaterThan: return furlang::ir::binary_op_instruction_t::GreaterThan; + case ast::binop_expression_node_t::LessEqual: return furlang::ir::binary_op_instruction_t::LessEq; + case ast::binop_expression_node_t::GreaterEqual: return furlang::ir::binary_op_instruction_t::GreaterEq; + case ast::binop_expression_node_t::None: + default: throw std::runtime_error("unreachable"); + } +} + +void ir_generator::visit_binop_expression_node(const ast::binop_expression_node& node) { + node.lhs()->accept(*this); + std::uint32_t lhs = m_registerCounter - 1; + node.rhs()->accept(*this); + std::uint32_t rhs = m_registerCounter - 1; + std::uint32_t dst = m_registerCounter++; + m_currentBlock->emplace(binary_op_instruction_t(node.type()), + ir::operand::new_reg(lhs), + ir::operand::new_reg(rhs), + ir::operand::new_reg(dst)); +} + +void ir_generator::visit_var_assign_expression_node(const ast::var_assign_expression_node& node) { + throw std::runtime_error("unimplemented"); +} + +} // namespace furc::front \ No newline at end of file diff --git a/furc/src/main.cpp b/furc/src/main.cpp index 0aab92e..42a9b43 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -1,14 +1,34 @@ -#ifndef LIBFURC +// #ifndef LIBFURC +#include "furc/front/ir_generator.hpp" #include "furc/front/parser.hpp" #include int main(void) { - furc::front::parser parser("", "func main() {\n return x = y;\n}"); - std::cout << parser.parse() << '\n'; + furc::front::parser parser("", "func main() {\n return 7 + 6 * 10;\n}"); + furc::front::ir_generator generator; + + auto program = parser.parse(); + if (program.has_error()) { + std::cerr << program << '\n'; + } + program->accept(generator); + + auto module = std::move(generator.move_module()); + std::cout << "Generated IR:\n"; + for (const auto& function : module.functions()) { + std::cout << function->name() << ":\n"; + furlang::ir::block_index blockIndex = 0; + for (const auto& block : function->blocks()) { + std::cout << " # block " << blockIndex++ << '\n'; + for (const auto& instruction : block->instructions()) { + std::cout << " " << *instruction << '\n'; + } + } + } return 0; } -#endif // LIBFURC \ No newline at end of file +// #endif // LIBFURC \ No newline at end of file diff --git a/furlang/include/furlang/ir/block.hpp b/furlang/include/furlang/ir/block.hpp index 3323552..2942760 100644 --- a/furlang/include/furlang/ir/block.hpp +++ b/furlang/include/furlang/ir/block.hpp @@ -13,13 +13,13 @@ namespace ir { // https://en.wikipedia.org/wiki/Basic_block class block { public: - using value_type = std::shared_ptr; + using value_type = std::unique_ptr; public: block() = default; public: template >> void emplace(Args&&... args) { - m_instructions.emplace_back(std::make_shared(std::forward(args)...)); + m_instructions.emplace_back(std::make_unique(std::forward(args)...)); } std::vector& instructions() { return m_instructions; } diff --git a/furlang/include/furlang/ir/function.hpp b/furlang/include/furlang/ir/function.hpp new file mode 100644 index 0000000..733d634 --- /dev/null +++ b/furlang/include/furlang/ir/function.hpp @@ -0,0 +1,35 @@ +#ifndef FURLANG_IR_FUNCTION_HPP +#define FURLANG_IR_FUNCTION_HPP + +#include "furlang/ir/block.hpp" + +#include +#include + +namespace furlang { +namespace ir { + +class function { +public: + using value_type = std::shared_ptr; +public: + function(const std::string& name) + : m_name(name) {} + + function(std::string&& name) + : m_name(std::move(name)) {} +public: + const std::string& name() const { return m_name; } + + value_type push() { return m_blocks.emplace_back(std::make_shared()); } + + const std::vector& blocks() const { return m_blocks; } +private: + std::string m_name; + std::vector m_blocks; +}; + +} // namespace ir +} // namespace furlang + +#endif // FURLANG_IR_FUNCTION_HPP \ No newline at end of file diff --git a/furlang/include/furlang/ir/instruction.hpp b/furlang/include/furlang/ir/instruction.hpp index b2bbc16..e3eb756 100644 --- a/furlang/include/furlang/ir/instruction.hpp +++ b/furlang/include/furlang/ir/instruction.hpp @@ -4,6 +4,8 @@ #include "furlang/ir/operand.hpp" #include +#include +#include namespace furlang { namespace ir { @@ -15,6 +17,7 @@ enum class instruction_t { Call, Branch, BranchCond, + Return, }; class instruction { @@ -28,6 +31,26 @@ public: instruction& operator=(const instruction&) = delete; public: virtual instruction_t type() const = 0; +public: + friend std::ostream& operator<<(std::ostream& os, const instruction& instruction) { return instruction.print(os); } +protected: + virtual std::ostream& print(std::ostream& os) const = 0; +}; + +class alloca_instruction final : public instruction { +public: + alloca_instruction() {} + + ~alloca_instruction() override = default; + + alloca_instruction(alloca_instruction&&) = default; + alloca_instruction& operator=(alloca_instruction&&) = default; + alloca_instruction(const alloca_instruction&) = delete; + alloca_instruction& operator=(const alloca_instruction&) = delete; +public: + instruction_t type() const override { return instruction_t::Alloca; } +protected: + std::ostream& print(std::ostream& os) const override { return os << "alloca"; } }; class assign_instruction final : public instruction { @@ -49,6 +72,10 @@ public: private: operand m_source; operand m_destination; +protected: + std::ostream& print(std::ostream& os) const override { + return os << "assign " << m_source << ", " << m_destination; + } }; enum class binary_op_instruction_t { @@ -66,6 +93,23 @@ enum class binary_op_instruction_t { GreaterEq, }; +static inline std::ostream& operator<<(std::ostream& os, binary_op_instruction_t type) { + switch (type) { + case binary_op_instruction_t::Add: return os << '+'; + case binary_op_instruction_t::Sub: return os << '-'; + case binary_op_instruction_t::Mul: return os << '*'; + case binary_op_instruction_t::Div: return os << '/'; + case binary_op_instruction_t::Mod: return os << '%'; + case binary_op_instruction_t::Eq: return os << "=="; + case binary_op_instruction_t::NotEq: return os << "!="; + case binary_op_instruction_t::LessThan: return os << '<'; + case binary_op_instruction_t::GreaterThan: return os << '>'; + case binary_op_instruction_t::LessEq: return os << "<="; + case binary_op_instruction_t::GreaterEq: return os << ">="; + } + return os; +} + class binary_op_instruction final : public instruction { public: binary_op_instruction(binary_op_instruction_t type, operand&& lhs, operand&& rhs, operand&& dst) @@ -89,6 +133,10 @@ private: operand m_lhs; operand m_rhs; operand m_dst; +protected: + std::ostream& print(std::ostream& os) const override { + return os << "binop(" << m_type << ") " << m_lhs << ", " << m_rhs << ", " << m_dst; + } }; using block_index = std::uint64_t; @@ -110,6 +158,8 @@ public: block_index block() const { return m_block; } private: block_index m_block; +protected: + std::ostream& print(std::ostream& os) const override { return os << "branch #" << m_block; } }; class branch_cond_instruction final : public instruction { @@ -133,6 +183,36 @@ private: operand m_condition; block_index m_ifBlock; block_index m_elseBlock; +protected: + std::ostream& print(std::ostream& os) const override { + return os << "branch_cond " << m_condition << ", #" << m_ifBlock << ", #" << m_elseBlock; + } +}; + +class return_instruction final : public instruction { +public: + return_instruction() {} + return_instruction(operand&& value) + : m_value(std::move(value)) {} + + ~return_instruction() override = default; + + return_instruction(return_instruction&&) = default; + return_instruction& operator=(return_instruction&&) = default; + return_instruction(const return_instruction&) = delete; + return_instruction& operator=(const return_instruction&) = delete; +public: + instruction_t type() const override { return instruction_t::Return; } + + const std::optional& value() const { return m_value; } +private: + std::optional m_value; +protected: + std::ostream& print(std::ostream& os) const override { + os << "return"; + if (m_value.has_value()) os << ' ' << m_value.value(); + return os; + } }; } // namespace ir diff --git a/furlang/include/furlang/ir/module.hpp b/furlang/include/furlang/ir/module.hpp new file mode 100644 index 0000000..1176a82 --- /dev/null +++ b/furlang/include/furlang/ir/module.hpp @@ -0,0 +1,32 @@ +#ifndef FURLANG_IR_MODULE_HPP +#define FURLANG_IR_MODULE_HPP + +#include "furlang/ir/function.hpp" + +#include +#include + +namespace furlang { +namespace ir { + +class module { +public: + using value_type = std::unique_ptr; +public: + module() = default; +public: + template + value_type& push(Args&&... args) { + return m_functions.emplace_back(std::forward(args)...); + } + + std::vector& functions() { return m_functions; } + const std::vector& functions() const { return m_functions; } +private: + std::vector m_functions; +}; + +} // namespace ir +} // namespace furlang + +#endif // FURLANG_IR_MODULE_HPP \ No newline at end of file diff --git a/furlang/include/furlang/ir/operand.hpp b/furlang/include/furlang/ir/operand.hpp index d1a14f4..7c2397f 100644 --- a/furlang/include/furlang/ir/operand.hpp +++ b/furlang/include/furlang/ir/operand.hpp @@ -2,6 +2,7 @@ #define FURLANG_IR_OPERAND_HPP #include +#include #include namespace furlang { @@ -97,6 +98,16 @@ public: register_operand reg() const { return m_value.reg; } integer_operand integer() const { return m_value.integer; } +public: + friend std::ostream& operator<<(std::ostream& os, const operand& operand) { + switch (operand.m_type) { + case operand_t::None: return os << "none"; + case operand_t::Register: return os << '%' << operand.m_value.reg; + case operand_t::Integer: return os << operand.m_value.integer; + case operand_t::String: return os << '"' << operand.m_value.string << '"'; + } + return os; + } private: operand() = default; private: