From 66a46d8b88df333059b5012fb68339f4f5cdca04 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Tue, 2 Jun 2026 13:57:23 +0200 Subject: [PATCH] docs: document IR --- furlang/include/furlang/ir/block.hpp | 51 ++- furlang/include/furlang/ir/function.hpp | 33 +- furlang/include/furlang/ir/instruction.hpp | 342 +++++++++++++++++---- furlang/include/furlang/ir/module.hpp | 24 +- furlang/include/furlang/ir/operand.hpp | 91 +++++- 5 files changed, 471 insertions(+), 70 deletions(-) diff --git a/furlang/include/furlang/ir/block.hpp b/furlang/include/furlang/ir/block.hpp index 6b0c8c1..289249b 100644 --- a/furlang/include/furlang/ir/block.hpp +++ b/furlang/include/furlang/ir/block.hpp @@ -10,13 +10,26 @@ namespace furlang { namespace ir { -// https://en.wikipedia.org/wiki/Basic_block +/** + * @brief Basic block. + * + * A basic block of IR instructions. https://en.wikipedia.org/wiki/Basic_block + */ class block { public: - using value_type = std::unique_ptr; + using value_type = std::unique_ptr; /**< Value type */ public: block() = default; public: + /** + * @brief Emplaces a new instruction. + * + * Emplaces a new instruction, if exit instruction hasn't been emplaced in this block yet. + * + * @tparam T Type of the instruction to emplace. + * @param args Arguments to call the constructor with. + * @return true if the instruction has been emplaced successfully. + */ template >> bool emplace(Args&&... args) { if (has_exit()) return false; @@ -29,11 +42,39 @@ public: return true; } - std::vector& instructions() { return m_instructions; } + /** + * @brief Returns this block's instructions. + * + * @return The instructions. + */ + std::vector& instructions() { return m_instructions; } + + /** + * @brief Returns this block's instructions. + * + * @return The instructions. + */ const std::vector& instructions() const { return m_instructions; } - bool has_exit() const { return m_exit != nullptr; } - value_type& exit() { return m_exit; } + /** + * @brief Checks whether an exit instruction has been emplaced in this block yet. + * + * @return true if the exit instruction has been emplaced. + */ + bool has_exit() const { return m_exit != nullptr; } + + /** + * @brief Returns this block's exit instruction. + * + * @return The exit instruction. + */ + value_type& exit() { return m_exit; } + + /** + * @brief Returns this block's exit instruction. + * + * @return The exit instruction. + */ const value_type& exit() const { return m_exit; } private: std::vector m_instructions; diff --git a/furlang/include/furlang/ir/function.hpp b/furlang/include/furlang/ir/function.hpp index 733d634..4835c18 100644 --- a/furlang/include/furlang/ir/function.hpp +++ b/furlang/include/furlang/ir/function.hpp @@ -9,20 +9,51 @@ namespace furlang { namespace ir { +/** + * @brief IR function. + * + * Consists of a name and blocks. + * @see block + */ class function { public: - using value_type = std::shared_ptr; + using value_type = std::shared_ptr; /**< Value type. */ public: + /** + * @brief Construct a new IR function. + * + * @param name Name to copy. + */ function(const std::string& name) : m_name(name) {} + /** + * @brief Construct a new IR function. + * + * @param name Name to move. + */ function(std::string&& name) : m_name(std::move(name)) {} public: + /** + * @brief Returns this function's name. + * + * @return The name. + */ const std::string& name() const { return m_name; } + /** + * @brief Pushes and returns a new IR block. + * + * @return The new IR block. + */ value_type push() { return m_blocks.emplace_back(std::make_shared()); } + /** + * @brief Returns this function's IR blocks. + * + * @return The IR blocks. + */ const std::vector& blocks() const { return m_blocks; } private: std::string m_name; diff --git a/furlang/include/furlang/ir/instruction.hpp b/furlang/include/furlang/ir/instruction.hpp index fed51f0..668c730 100644 --- a/furlang/include/furlang/ir/instruction.hpp +++ b/furlang/include/furlang/ir/instruction.hpp @@ -10,16 +10,25 @@ namespace furlang { namespace ir { +/** + * @brief IR instruction type. + */ enum class instruction_t { - Alloca, - Assign, - BinaryOp, - Call, - Branch, - BranchCond, - Return, + Alloca, /**< Unused */ + Assign, /**< Assign */ + BinaryOp, /**< Binary operation */ + Call, /**< Call */ + Branch, /**< Branch */ + BranchCond, /**< Conditional branch */ + Return, /**< Return */ }; +/** + * @brief Checks if an instruction type exits. + * + * @param type Instruction type. + * @return true if the instruction type exits. + */ static inline bool is_exit_instruction(instruction_t type) { switch (type) { case instruction_t::Branch: @@ -29,54 +38,137 @@ static inline bool is_exit_instruction(instruction_t type) { } } +/** + * @brief IR instruction + */ class instruction { public: instruction() = default; virtual ~instruction() = default; - instruction(instruction&&) = default; - instruction& operator=(instruction&&) = default; - instruction(const instruction&) = delete; + /** + * @brief Move constructor + */ + instruction(instruction&&) = default; + + /** + * @brief Move constructor + */ + instruction& operator=(instruction&&) = default; + + instruction(const instruction&) = delete; + instruction& operator=(const instruction&) = delete; public: + /** + * @brief Returns this instruction's type. + * + * @return The type. + */ virtual instruction_t type() const = 0; public: + /** + * @brief Prints an instruction to an output stream. + * + * Equivalent to calling instruction.print(os). + * + * @param os Output stream. + * @param instruction Instruction to print. + * @return The output stream. + */ friend std::ostream& operator<<(std::ostream& os, const instruction& instruction) { return instruction.print(os); } protected: + /** + * @brief Prints this instruction to an output stream. + * + * @param os Output stream. + * @return The output stream. + */ virtual std::ostream& print(std::ostream& os) const = 0; }; +/** + * @brief Alloca instruction + */ 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; + /** + * @brief Move constructor + */ + alloca_instruction(alloca_instruction&&) = default; + + /** + * @brief Move constructor + */ + alloca_instruction& operator=(alloca_instruction&&) = default; + + alloca_instruction(const alloca_instruction&) = delete; + alloca_instruction& operator=(const alloca_instruction&) = delete; public: + /** + * @brief Returns this instruction's type. + * + * @return instruction_t::Alloca. + */ instruction_t type() const override { return instruction_t::Alloca; } protected: std::ostream& print(std::ostream& os) const override { return os << "alloca"; } }; +/** + * @brief Assign instruction + */ class assign_instruction final : public instruction { public: + /** + * @brief Construct a new assign instruction. + * + * @param src Source operand. + * @param dst Destination operand. + */ assign_instruction(operand&& src, operand&& dst) : m_source(std::move(src)), m_destination(std::move(dst)) {} ~assign_instruction() override = default; - assign_instruction(assign_instruction&&) = default; - assign_instruction& operator=(assign_instruction&&) = default; - assign_instruction(const assign_instruction&) = delete; + /** + * @brief Move constructor + */ + assign_instruction(assign_instruction&&) = default; + + /** + * @brief Move constructor + */ + assign_instruction& operator=(assign_instruction&&) = default; + + assign_instruction(const assign_instruction&) = delete; + assign_instruction& operator=(const assign_instruction&) = delete; public: + /** + * @brief Returns this instruction's type. + * + * @return instruction_t::Assign. + */ instruction_t type() const override { return instruction_t::Assign; } + /** + * @brief Returns this instruction's source. + * + * @return The source. + */ const operand& source() const { return m_source; } + + /** + * @brief Returns this instruction's destination. + * + * @return The destination. + */ const operand& destination() const { return m_destination; } private: operand m_source; @@ -87,19 +179,22 @@ protected: } }; +/** + * @brief Binary operation instruction type + */ enum class binary_op_instruction_t { - Add, - Sub, - Mul, - Div, - Mod, + Add, /**< Addition */ + Sub, /**< Subtraction */ + Mul, /**< Multiplication */ + Div, /**< Division */ + Mod, /**< Modulo */ - Eq, - NotEq, - LessThan, - GreaterThan, - LessEq, - GreaterEq, + Eq, /**< Equal */ + NotEq, /**< Not equal */ + LessThan, /**< Less than */ + GreaterThan, /**< Greater than */ + LessEq, /**< Less or equal */ + GreaterEq, /**< Greater or equal */ }; static inline std::ostream& operator<<(std::ostream& os, binary_op_instruction_t type) { @@ -119,103 +214,242 @@ static inline std::ostream& operator<<(std::ostream& os, binary_op_instruction_t return os; } +/** + * @brief Binary operation instruction + */ class binary_op_instruction final : public instruction { public: + /** + * @brief Construct a new binary operation instruction. + * + * @param type Operation type. + * @param lhs Left-hand-side operand. + * @param rhs Right-hand-side operand. + * @param dst Destination operand. + */ binary_op_instruction(binary_op_instruction_t type, operand&& lhs, operand&& rhs, operand&& dst) : m_type(type), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)), m_dst(std::move(dst)) {} ~binary_op_instruction() override = default; - binary_op_instruction(binary_op_instruction&&) = default; - binary_op_instruction& operator=(binary_op_instruction&&) = default; - binary_op_instruction(const binary_op_instruction&) = delete; + /** + * @brief Move constructor + */ + binary_op_instruction(binary_op_instruction&&) = default; + + /** + * @brief Move constructor + */ + binary_op_instruction& operator=(binary_op_instruction&&) = default; + + binary_op_instruction(const binary_op_instruction&) = delete; + binary_op_instruction& operator=(const binary_op_instruction&) = delete; public: + /** + * @brief Returns this instruction's type. + * + * @return instruction_t::BinaryOp. + */ instruction_t type() const override { return instruction_t::BinaryOp; } + /** + * @brief Returns this instruction's operation type. + * + * @return The operation type. + */ binary_op_instruction_t op_type() const { return m_type; } - const operand& lhs() const { return m_lhs; } - const operand& rhs() const { return m_rhs; } - const operand& dst() const { return m_dst; } + + /** + * @brief Returns this instruction's left-hand-side operand. + * + * @return The operand. + */ + const operand& lhs() const { return m_lhs; } + + /** + * @brief Returns this instruction's right-hand-side operand. + * + * @return The operand. + */ + const operand& rhs() const { return m_rhs; } + + /** + * @brief Returns this instruction's destination operand. + * + * @return The operand. + */ + const operand& dst() const { return m_dst; } private: binary_op_instruction_t m_type; - operand m_lhs; - operand m_rhs; - operand m_dst; + operand m_lhs /**< Left-hand-side operand */; + operand m_rhs /**< Right-hand-side operand */; + operand m_dst /**< Destination operand */; 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; +using block_index = std::uint64_t; /**< IR block index alias */ +/** + * @brief Branch instruction + */ class branch_instruction final : public instruction { public: + /** + * @brief Construct a new branch instruction. + * + * @param block Destination block index. + */ branch_instruction(block_index block) : m_block(block) {} ~branch_instruction() override = default; - branch_instruction(branch_instruction&&) = default; - branch_instruction& operator=(branch_instruction&&) = default; - branch_instruction(const branch_instruction&) = delete; + /** + * @brief Move constructor. + */ + branch_instruction(branch_instruction&&) = default; + + /** + * @brief Move constructor. + */ + branch_instruction& operator=(branch_instruction&&) = default; + + branch_instruction(const branch_instruction&) = delete; + branch_instruction& operator=(const branch_instruction&) = delete; public: + /** + * @brief Returns this instruction's type. + * + * @return instruction_t::Branch. + */ instruction_t type() const override { return instruction_t::Branch; } + /** + * @brief Returns this instruction's destination block index. + * + * @return The destination block index. + */ block_index block() const { return m_block; } private: - block_index m_block; + block_index m_block; /**< Destination block index. */ protected: std::ostream& print(std::ostream& os) const override { return os << "branch #" << m_block; } }; +/** + * @brief Conditional branch instruction + */ class branch_cond_instruction final : public instruction { public: + /** + * @brief Construct a new conditional branch instruction. + * + * @param condition Condition operand. + * @param ifBlock Destination block index. + * @param elseBlock Else block index. + */ branch_cond_instruction(operand&& condition, block_index ifBlock, block_index elseBlock) : m_condition(std::move(condition)), m_ifBlock(ifBlock), m_elseBlock(elseBlock) {} ~branch_cond_instruction() override = default; - branch_cond_instruction(branch_cond_instruction&&) = default; - branch_cond_instruction& operator=(branch_cond_instruction&&) = default; - branch_cond_instruction(const branch_cond_instruction&) = delete; + /** + * @brief Move constructor. + */ + branch_cond_instruction(branch_cond_instruction&&) = default; + + /** + * @brief Move constructor. + */ + branch_cond_instruction& operator=(branch_cond_instruction&&) = default; + + branch_cond_instruction(const branch_cond_instruction&) = delete; + branch_cond_instruction& operator=(const branch_cond_instruction&) = delete; public: instruction_t type() const override { return instruction_t::BranchCond; } + /** + * @brief Returns this instruction's condition operand. + * + * @return The operand. + */ const operand& condition() const { return m_condition; } - block_index if_block() const { return m_ifBlock; } - block_index else_block() const { return m_elseBlock; } + + /** + * @brief Returns this instruction's destination block index. + * + * @return The destination block index. + */ + block_index if_block() const { return m_ifBlock; } + + /** + * @brief Returns this instruction's else block index. + * + * @return The else block index. + */ + block_index else_block() const { return m_elseBlock; } private: - operand m_condition; - block_index m_ifBlock; - block_index m_elseBlock; + operand m_condition /**< Condition operand. */; + block_index m_ifBlock /**< Destination block index. */; + block_index m_elseBlock /**< Else block index. */; protected: std::ostream& print(std::ostream& os) const override { return os << "branch_cond " << m_condition << ", #" << m_ifBlock << ", #" << m_elseBlock; } }; +/** + * @brief Return instruction + */ class return_instruction final : public instruction { public: - return_instruction() {} + return_instruction() = default; + + /** + * @brief Construct a new return instruction. + * + * @param value Return value operand. + */ 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; + /** + * @brief Move constructor. + */ + return_instruction(return_instruction&&) = default; + + /** + * @brief Move constructor. + */ + return_instruction& operator=(return_instruction&&) = default; + + return_instruction(const return_instruction&) = delete; + return_instruction& operator=(const return_instruction&) = delete; public: + /** + * @brief Returns this instruction's type. + * + * @return instruction_t::Return. + */ instruction_t type() const override { return instruction_t::Return; } + /** + * @brief Returns this instruction's return value operand. + * + * @return The operand. + */ const std::optional& value() const { return m_value; } private: - std::optional m_value; + std::optional m_value; /**< The return value operand. */ protected: std::ostream& print(std::ostream& os) const override { os << "return"; diff --git a/furlang/include/furlang/ir/module.hpp b/furlang/include/furlang/ir/module.hpp index 1176a82..90e2a3f 100644 --- a/furlang/include/furlang/ir/module.hpp +++ b/furlang/include/furlang/ir/module.hpp @@ -9,18 +9,38 @@ namespace furlang { namespace ir { +/** + * @brief IR module + */ class module { public: - using value_type = std::unique_ptr; + using value_type = std::unique_ptr; /**< Value type. */ public: module() = default; public: + /** + * @brief Pushes and returns a new IR function. + * + * @param args Arguments to call the constructor with. + * @return The new IR function. + */ template value_type& push(Args&&... args) { return m_functions.emplace_back(std::forward(args)...); } - std::vector& functions() { return m_functions; } + /** + * @brief Returns this module's functions. + * + * @return The functions. + */ + std::vector& functions() { return m_functions; } + + /** + * @brief Returns this module's functions. + * + * @return The functions. + */ const std::vector& functions() const { return m_functions; } private: std::vector m_functions; diff --git a/furlang/include/furlang/ir/operand.hpp b/furlang/include/furlang/ir/operand.hpp index 4baf57b..dff75f3 100644 --- a/furlang/include/furlang/ir/operand.hpp +++ b/furlang/include/furlang/ir/operand.hpp @@ -8,19 +8,41 @@ namespace furlang { namespace ir { +/** + * @brief Operand type + */ enum class operand_t { - None, - Register, - Variable, - Integer, - String, + None, /**< None */ + Register, /**< Register */ + Variable, /**< Variable */ + Integer, /**< Integer */ + String, /**< String */ }; +/** + * @brief Register operand alias. + * @see operand_t::Register + */ using register_operand = std::uint32_t; +/** + * @brief Variable operand alias. + * @see operand_t::Variable + */ using variable_operand = std::string; -using integer_operand = std::uint64_t; -using string_operand = std::string; +/** + * @brief Integer operand alias. + * @see operand_t::Integer + */ +using integer_operand = std::uint64_t; +/** + * @brief String operand alias. + * @see operand_t::String + */ +using string_operand = std::string; +/** + * @brief IR operand + */ class operand { public: ~operand() { @@ -29,6 +51,9 @@ public: } } + /** + * @brief Move constructor. + */ operand(operand&& other) noexcept : m_type(other.m_type) { switch (m_type) { @@ -49,6 +74,9 @@ public: other.m_value.destroy(other.m_type); } + /** + * @brief Move constructor. + */ operand& operator=(operand&& other) noexcept { if (this == &other) return *this; m_type = other.m_type; @@ -74,6 +102,12 @@ public: operand(const operand&) = delete; operand& operator=(const operand&) = delete; public: + /** + * @brief Construct a new register operand. + * + * @param value Value of the new register operand. + * @return The register operand. + */ static operand new_reg(register_operand value) { operand operand; operand.m_type = operand_t::Register; @@ -81,6 +115,12 @@ public: return operand; } + /** + * @brief Construct a new variable operand. + * + * @param value Value of the new variable operand. + * @return The variable operand. + */ template static operand new_variable(T&& value) { operand operand; @@ -89,6 +129,12 @@ public: return operand; } + /** + * @brief Construct a new integer operand. + * + * @param value Value of the new integer operand. + * @return The integer operand. + */ static operand new_integer(integer_operand value) { operand operand; operand.m_type = operand_t::Integer; @@ -96,6 +142,12 @@ public: return operand; } + /** + * @brief Construct a new string operand. + * + * @param value Value of the new string operand. + * @return The string operand. + */ template static operand new_string(T&& value) { operand operand; @@ -104,11 +156,34 @@ public: return operand; } public: + /** + * @brief Returns this operand's type. + * + * @return The operand type. + */ operand_t type() const { return m_type; } + /** + * @brief Returns this operand's register value. + * + * @return The register value. + */ register_operand reg() const { return m_value.reg; } - integer_operand integer() const { return m_value.integer; } + + /** + * @brief Returns this operand's integer value. + * + * @return The integer value. + */ + integer_operand integer() const { return m_value.integer; } public: + /** + * @brief Prints an operand to an output stream. + * + * @param os Output stream. + * @param operand Operand to print. + * @return The output stream. + */ friend std::ostream& operator<<(std::ostream& os, const operand& operand) { switch (operand.m_type) { case operand_t::None: return os << "none";