diff --git a/furc/include/furc/back/ir.hpp b/furc/include/furc/back/ir.hpp new file mode 100644 index 0000000..7ca4ee9 --- /dev/null +++ b/furc/include/furc/back/ir.hpp @@ -0,0 +1,406 @@ +#ifndef FURC_BACK_IR_HPP +#define FURC_BACK_IR_HPP + +#include "furc/front/ast.hpp" +#include "furlang/arena.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace furc { + +struct ir_operand { + enum type_e { + Integer = 0, + Register, + Variable, + Function, + Block, + BlockPair, + PhiPair, + } type; + union value_u { + std::uint64_t integer; + struct register_s { + std::uint64_t name : 54; + std::uint64_t ver : 10; + } reg; + std::uint16_t variable; + std::uint64_t function; + std::uint64_t block; + struct block_pair_s { + std::uint64_t first; + std::uint64_t second; + } blockPair; + struct phi_pair_s { + register_s reg; + std::uint64_t block; + } phiPair; + + value_u() = default; + + value_u(std::uint64_t integer) + : integer(integer) {} + + value_u(std::uint16_t variable) + : variable(variable) {} + + value_u(std::uint64_t first, std::uint64_t second) + : blockPair({ first, second }) {} + + value_u(register_s reg, std::uint64_t block) + : phiPair({ reg, block }) {} + } value; + + template >> + ir_operand(type_e type, Args&&... args) + : type(type), value(std::forward(args)...) {} +}; + +struct ir_type { + enum type_e { + Void = 0, + S8, + U8, + S16, + U16, + S32, + U32, + S64, + U64, + } type = Void; +}; + +// TODO: Add data types to instructions (like mov QWORD ... in x86 assembly) +struct ir_instruction { + enum type_e { + Move = 0, + Call, + Branch, + BranchCond, + Return, + Phi, + + Add, + Sub, + Mul, + Div, + Mod, + + Shl, + Shr, + BinAnd, + BinOr, + BinXor, + And, + Or, + + Eq, + NotEq, + LessThan, + LessEq, + GreaterThan, + GreaterEq, + + Positive, + Negative, + Increment, + Decrement, + BinNot, + Not, + + Sizeof, + Pointerof, + Lenof, + } type; + std::optional destination; + std::vector sources; + + ir_instruction(type_e type, + std::optional destination = {}, + std::initializer_list sources = {}) + : type(type), destination(destination), sources(sources) {} + + static constexpr bool is_terminating(type_e type) { + switch (type) { + case Branch: + case BranchCond: + case Return: return true; + default: return false; + } + } +}; + +struct ir_basic_block { + std::vector instructions; + + bool is_terminated() const { + return !instructions.empty() && ir_instruction::is_terminating(instructions.back().type); + } +}; + +struct ir_variable { + ir_variable() = default; + + ir_variable(ir_type type) + : type(type) {} + + virtual ~ir_variable() = default; + + ir_variable(ir_variable&&) noexcept = default; + ir_variable& operator=(ir_variable&&) noexcept = default; + + ir_variable(const ir_variable&) = default; + ir_variable& operator=(const ir_variable&) = default; + + ir_type type; + + virtual ir_operand operand() const = 0; +}; + +struct ir_module_variable : ir_variable { + ir_module_variable(ir_type type, std::uint16_t name) + : ir_variable(type), name(name) {} + + std::uint16_t name; + + ir_operand operand() const final { return { ir_operand::Variable, name }; } +}; + +struct ir_function_variable : ir_variable { + ir_function_variable(ir_type type, std::uint64_t name) + : ir_variable(type), name(name) {} + + std::uint64_t name; + + ir_operand operand() const final { return { ir_operand::Register, name }; } +}; + +struct ir_scope { + ir_scope() = default; + virtual ~ir_scope() = default; + + ir_scope(ir_scope&&) noexcept = default; + ir_scope& operator=(ir_scope&&) noexcept = default; + + ir_scope(const ir_scope&) = default; + ir_scope& operator=(const ir_scope&) = default; + + ir_scope* previous = nullptr; + + std::unordered_map variables; + + const ir_variable* variable(const std::string& name) const { + if (auto it = variables.find(name); it != variables.end()) return it->second; + return (previous != nullptr) ? previous->variable(name) : nullptr; + } + + virtual const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) = 0; +}; + +struct ir_function : ir_scope { + enum type_e { + Normal = 0, + Import, + Native, + } type = Normal; + enum access_e { + Public = 0, + Private, + } access = Public; + + std::string name; + std::vector params; + ir_type retType; + std::vector blocks; + + std::uint64_t regCount = 0; + + const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) final { + return variables[name] = arena.allocate(type, regCount++); + } + + static ir_function from_name(std::string&& name) { + ir_function func; + func.name = std::move(name); + return func; + } +}; + +struct ir_module : ir_scope { + std::vector functions; + furlang::arena arena; + + std::uint16_t varCount = 0; + + const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) final { + return variables[name] = arena.allocate(type, varCount); + } + + ir_function* add_function(ir_function&& function) { + return functions.emplace_back(arena.allocate(std::move(function))); + } +}; + +struct ir_context { + ir_context(ir_function* function) + : function(function) { + if (function->blocks.empty()) new_last(); + blockPtr = &function->blocks.front(); + } + + ~ir_context() { + if (blockPtr == nullptr) return; + if (!blockPtr->is_terminated()) { + if (blockIdx + 1 == function->blocks.size()) { + add_instr(ir_instruction::Return); + } else { + add_instr(ir_instruction::Branch, ir_operand{ ir_operand::Block, blockIdx + 1 }); + } + } + } + + ir_context(ir_context&& other) noexcept + : function(other.function), blockIdx(other.blockIdx), blockPtr(other.blockPtr) { + other.function = nullptr; + other.blockIdx = 0; + other.blockPtr = nullptr; + } + + ir_context& operator=(ir_context&& other) noexcept { + if (this == &other) return *this; + function = other.function; + blockIdx = other.blockIdx; + blockPtr = other.blockPtr; + + other.function = nullptr; + other.blockIdx = 0; + other.blockPtr = nullptr; + return *this; + } + + ir_context(const ir_context&) = delete; + ir_context& operator=(const ir_context&) = delete; + + template >> + ir_instruction& add_instr(Args&&... args) { + auto it = blockPtr->instructions.end(); + if (!blockPtr->instructions.empty() && ir_instruction::is_terminating(blockPtr->instructions.back().type)) --it; + it = blockPtr->instructions.emplace(it, std::forward(args)...); + if (ir_instruction::is_terminating(it->type) && it + 1 != blockPtr->instructions.end()) + blockPtr->instructions.pop_back(); + return *it; + } + + void terminate() { add_instr(ir_instruction::Return); } + + void terminate(ir_operand value) { add_instr(ir_instruction::Return, value); } + + void terminate(std::uint64_t block) { add_instr(ir_instruction::Branch, ir_operand{ ir_operand::Block, block }); } + + ir_instruction* terminate(ir_operand cond, std::uint64_t thenBranch, std::uint64_t elseBranch) { + return &add_instr(ir_instruction{ ir_instruction::BranchCond, + ir_operand{ ir_operand::BlockPair, thenBranch, elseBranch }, + { cond } }); + } + + ir_context& new_next() { + if (blockPtr->instructions.empty()) return *this; + auto it = function->blocks.begin() + static_cast(++blockIdx); + if (!blockPtr->is_terminated()) terminate(blockIdx); + blockPtr = &*function->blocks.emplace(it); + return *this; + } + + ir_context& new_last() { + blockIdx = function->blocks.size(); + blockPtr = &*function->blocks.emplace(function->blocks.end()); + return *this; + } + + ir_context& go(std::uint64_t block) { + blockIdx = std::min(block, function->blocks.size() - 1); + blockPtr = function->blocks.data() + static_cast(blockIdx); + return *this; + } + + ir_context& go_next() { return go(blockIdx + 1); } + ir_context& go_last() { return go(std::min(0, function->blocks.size() - 1)); } + + ir_operand last_register() const { return { ir_operand::Register, function->regCount - 1 }; } + ir_operand next_register() const { return { ir_operand::Register, function->regCount++ }; } + + static ir_operand block_op(std::uint64_t blockIdx) { return { ir_operand::Block, blockIdx }; } + + ir_function* function = nullptr; + std::uint64_t blockIdx = 0; + ir_basic_block* blockPtr = nullptr; +}; + +class ir_generator final : public ast_visitor { +public: + ir_generator() + : m_initContext(m_module.add_function(ir_function::from_name("module$init"))) {} + + void finalize() { + m_module.functions.front()->blocks.emplace_back().instructions.push_back( + ir_instruction{ ir_instruction::Return }); + } + + ir_module build() { + m_scope = nullptr; + m_context = {}; + m_initContext.blockPtr = nullptr; + return std::move(m_module); + } + + static ir_module generate(const ast_node& node) { + ir_generator gen; + node.accept(gen); + gen.finalize(); + return gen.build(); + } + + static ir_module generate(const ast& tree) { + ir_generator gen; + for (const auto& node : tree.decls) + node->accept(gen); + gen.finalize(); + return gen.build(); + } +private: + void visit_comp_stmt_node(const comp_stmt_node& node) override; + void visit_if_stmt_node(const if_stmt_node& node) override; + void visit_while_stmt_node(const while_stmt_node& node) override; + void visit_return_stmt_node(const return_stmt_node& node) override; + void visit_var_decl_node(const var_decl_node& node) override; + void visit_func_decl_node(const func_decl_node& node) override; + void visit_var_read_expr_node(const var_read_expr_node& node) override; + void visit_func_call_expr_node(const func_call_expr_node& node) override; + void visit_group_expr_node(const group_expr_node& node) override; + void visit_binary_op_expr_node(const binary_op_expr_node& node) override; + void visit_unary_op_expr_node(const unary_op_expr_node& node) override; + void visit_if_expr_node(const if_expr_node& node) override; + void visit_int_lit_node(const int_lit_node& node) override; + void visit_char_lit_node(const char_lit_node& node) override; +private: + ir_context& context() { return m_context.top(); } +private: + ir_module m_module; + ir_scope* m_scope = &m_module; + std::stack m_context; + + ir_context m_initContext; +}; + +} // namespace furc + +#endif // FURC_BACK_IR_HPP diff --git a/furc/src/back/ir.cpp b/furc/src/back/ir.cpp new file mode 100644 index 0000000..42a5365 --- /dev/null +++ b/furc/src/back/ir.cpp @@ -0,0 +1,254 @@ +#include "furc/back/ir.hpp" + +#include "furc/front/ast.hpp" + +#include +#include + +namespace furc { + +static ir_type ast_type_to_ir(const ast_type& type) { + switch (type.type) { + case ast_type::Void: return { ir_type::Void }; + case ast_type::S8: return { ir_type::S8 }; + case ast_type::U8: return { ir_type::U8 }; + case ast_type::S16: return { ir_type::S16 }; + case ast_type::U16: return { ir_type::U16 }; + case ast_type::S32: return { ir_type::S32 }; + case ast_type::U32: return { ir_type::U32 }; + case ast_type::S64: return { ir_type::S64 }; + case ast_type::U64: return { ir_type::U64 }; + } + throw std::runtime_error("unreachable"); +} + +void ir_generator::visit_comp_stmt_node(const comp_stmt_node& node) { + // TODO: Introduce scopes for statements to naturally allow variable shadowing. + for (const auto& stmt : node.stmts) { + stmt->accept(*this); + } +} + +void ir_generator::visit_if_stmt_node(const if_stmt_node& node) { + node.cond->accept(*this); + auto* branch = context().terminate(context().last_register(), context().blockIdx + 1, 0); + + context().new_next(); + node.thenBranch->accept(*this); + context().new_next(); + branch->destination->value.blockPair.second = context().blockIdx; // NOLINT + if (node.elseBranch != nullptr) { + node.elseBranch->accept(*this); + context().new_next(); + } +} + +void ir_generator::visit_while_stmt_node(const while_stmt_node& node) { + context().new_next(); + auto header = context().blockIdx; + node.cond->accept(*this); + auto* branch = context().terminate(context().last_register(), context().blockIdx + 1, 0); + + context().new_next(); + auto body = context().blockIdx; + node.body->accept(*this); + if (!context().blockPtr->is_terminated()) context().terminate(header); + + context().new_next(); + branch->destination->value.blockPair.second = context().blockIdx; // NOLINT +} + +void ir_generator::visit_return_stmt_node(const return_stmt_node& node) { + if (node.value == nullptr) { + context().terminate(); + } else { + node.value->accept(*this); + context().terminate(context().last_register()); + } +} + +void ir_generator::visit_var_decl_node(const var_decl_node& node) { + const auto* var = m_scope->allocate(m_module.arena, node.name, ast_type_to_ir(node.type)); + + if (node.init != nullptr) { + if (m_context.empty()) { + m_initContext.new_next(); + m_context.push(std::move(m_initContext)); + node.init->accept(*this); + context().add_instr(ir_instruction{ ir_instruction::Move, var->operand(), { context().last_register() } }); + m_initContext = std::move(m_context.top()); + m_context.pop(); + } else { + node.init->accept(*this); + context().add_instr(ir_instruction{ ir_instruction::Move, var->operand(), { context().last_register() } }); + } + } +} + +void ir_generator::visit_func_decl_node(const func_decl_node& node) { + ir_function function; + function.previous = m_scope; + function.name = node.name; + for (const auto& param : node.params) { + function.params.push_back(ast_type_to_ir(param.type)); + function.allocate(m_module.arena, param.name, ast_type_to_ir(param.type)); + } + function.retType = ast_type_to_ir(node.type); + + if (node.def.has_value()) { + auto* func = m_module.functions.emplace_back(m_module.arena.allocate(std::move(function))); + m_context.emplace(func); + m_scope = func; + node.def->body.accept(*this); + m_scope = m_scope->previous; + m_context.pop(); + } +} + +void ir_generator::visit_var_read_expr_node(const var_read_expr_node& node) { + const auto* var = m_scope->variable(node.name); + if (var == nullptr) throw std::runtime_error("unknown variable"); + context().add_instr(ir_instruction{ ir_instruction::Move, context().next_register(), { var->operand() } }); +} + +void ir_generator::visit_func_call_expr_node(const func_call_expr_node& node) { + throw std::runtime_error("not implemented"); +} + +void ir_generator::visit_group_expr_node(const group_expr_node& node) { + node.inner->accept(*this); +} + +void ir_generator::visit_binary_op_expr_node(const binary_op_expr_node& node) { + node.lhs->accept(*this); + auto lhs = context().last_register(); + node.rhs->accept(*this); + auto rhs = context().last_register(); + + switch (node.type) { + case binary_op_expr_node::Add: + context().add_instr(ir_instruction{ ir_instruction::Add, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::Sub: + context().add_instr(ir_instruction{ ir_instruction::Sub, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::Mul: + context().add_instr(ir_instruction{ ir_instruction::Mul, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::Div: + context().add_instr(ir_instruction{ ir_instruction::Div, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::Mod: + context().add_instr(ir_instruction{ ir_instruction::Mod, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::Shl: + context().add_instr(ir_instruction{ ir_instruction::Shl, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::Shr: + context().add_instr(ir_instruction{ ir_instruction::Shr, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::BinAnd: + context().add_instr(ir_instruction{ ir_instruction::BinAnd, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::BinOr: + context().add_instr(ir_instruction{ ir_instruction::BinOr, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::BinXor: + context().add_instr(ir_instruction{ ir_instruction::BinXor, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::And: + context().add_instr(ir_instruction{ ir_instruction::And, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::Or: + context().add_instr(ir_instruction{ ir_instruction::Or, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::Equals: + context().add_instr(ir_instruction{ ir_instruction::Eq, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::NotEquals: + context().add_instr(ir_instruction{ ir_instruction::NotEq, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::LessThan: + context().add_instr(ir_instruction{ ir_instruction::LessThan, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::LessEquals: + context().add_instr(ir_instruction{ ir_instruction::LessEq, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::GreaterThan: + context().add_instr(ir_instruction{ ir_instruction::GreaterThan, context().next_register(), { lhs, rhs } }); + return; + case binary_op_expr_node::GreaterEquals: + context().add_instr(ir_instruction{ ir_instruction::GreaterEq, context().next_register(), { lhs, rhs } }); + return; + } + throw std::runtime_error("unreachable"); +} + +void ir_generator::visit_unary_op_expr_node(const unary_op_expr_node& node) { + if (node.type == unary_op_expr_node::PostInc || node.type == unary_op_expr_node::PostDec) { + node.lhs->accept(*this); + auto lhs = context().last_register(); + node.lhs->accept(*this); + context().add_instr(node.type == unary_op_expr_node::PostInc ? ir_instruction::Increment + : ir_instruction::Decrement, + context().last_register()); + return; + } + + node.lhs->accept(*this); + auto lhs = context().last_register(); + + switch (node.type) { + case unary_op_expr_node::Positive: context().add_instr(ir_instruction::Positive, lhs); + case unary_op_expr_node::Negative: context().add_instr(ir_instruction::Negative, lhs); + case unary_op_expr_node::PreInc: context().add_instr(ir_instruction::Increment, lhs); + case unary_op_expr_node::PreDec: context().add_instr(ir_instruction::Decrement, lhs); + case unary_op_expr_node::BinNot: context().add_instr(ir_instruction::BinNot, lhs); + case unary_op_expr_node::Not: context().add_instr(ir_instruction::Not, lhs); + case unary_op_expr_node::Sizeof: context().add_instr(ir_instruction::Sizeof, lhs); + case unary_op_expr_node::Pointerof: context().add_instr(ir_instruction::Pointerof, lhs); + case unary_op_expr_node::Lengthof: context().add_instr(ir_instruction::Lenof, lhs); + case unary_op_expr_node::PostInc: + case unary_op_expr_node::PostDec: return; + } + throw std::runtime_error("unreachable"); +} + +void ir_generator::visit_if_expr_node(const if_expr_node& node) { + node.cond->accept(*this); + auto* branch = context().terminate(context().last_register(), context().blockIdx + 1, 0); + + context().new_next(); + auto thenBranch = context().blockIdx; + node.thenExpr->accept(*this); + auto thenReg = context().last_register(); + + context().new_next(); + branch->destination->value.blockPair.second = context().blockIdx; // NOLINT + node.elseExpr->accept(*this); + auto elseReg = context().last_register(); + auto resReg = context().next_register(); + context().add_instr(ir_instruction{ ir_instruction::Move, resReg, { elseReg } }); + + context().new_next(); + auto epilogue = context().blockIdx; + + context().go(thenBranch); + context().add_instr(ir_instruction{ ir_instruction::Move, resReg, { thenReg } }); + context().terminate(epilogue); + context().go(epilogue); +} + +void ir_generator::visit_int_lit_node(const int_lit_node& node) { + context().add_instr(ir_instruction{ ir_instruction::Move, + context().next_register(), + { ir_operand{ ir_operand::Integer, node.value } } }); +} + +void ir_generator::visit_char_lit_node(const char_lit_node& node) { + context().add_instr(ir_instruction{ ir_instruction::Move, + context().next_register(), + { ir_operand{ ir_operand::Integer, static_cast(node.value) } } }); +} + +} // namespace furc diff --git a/furc/src/main.cpp b/furc/src/main.cpp index 1b679a5..8249a4e 100644 --- a/furc/src/main.cpp +++ b/furc/src/main.cpp @@ -1,3 +1,4 @@ +#include "furc/back/ir.hpp" #include "furc/front/lexer.hpp" #include "furc/front/parser.hpp" #include "furlang/arena.hpp" @@ -6,17 +7,15 @@ int main(void) { furlang::arena arena; std::string_view content = R"( - func main(argc: u64) -> s32 pre(arc > 1) { + func main(argc: u64) -> s32 { x: s32 = 1 + 2 * 3; - println(x); return if (x == 9) 1 else 0; } )"; - furc::lexer lexer = { "", content }; - furc::parser parser = { std::move(lexer), arena }; - - auto program = parser.parse(); + furc::lexer lexer = { "", content }; + furc::parser parser = { std::move(lexer), arena }; + furc::ir_module irModule = furc::ir_generator::generate(parser.parse()); return 0; } diff --git a/furlang/include/furlang/ir/block.hpp b/furlang/include/furlang/ir/block.hpp deleted file mode 100644 index 289249b..0000000 --- a/furlang/include/furlang/ir/block.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef FURLANG_IR_BLOCK_HPP -#define FURLANG_IR_BLOCK_HPP - -#include "furlang/ir/instruction.hpp" - -#include -#include -#include - -namespace furlang { -namespace ir { - -/** - * @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; /**< 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; - 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; - } - - /** - * @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; } - - /** - * @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; - value_type m_exit; -}; - -} // namespace ir -} // namespace furlang - -#endif // FURLANG_IR_BLOCK_HPP \ No newline at end of file diff --git a/furlang/include/furlang/ir/function.hpp b/furlang/include/furlang/ir/function.hpp deleted file mode 100644 index 9a434fe..0000000 --- a/furlang/include/furlang/ir/function.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef FURLANG_IR_FUNCTION_HPP -#define FURLANG_IR_FUNCTION_HPP - -#include "furlang/ir/block.hpp" - -#include -#include -#include -#include - -namespace furlang { -namespace ir { - -enum class function_t : std::uint8_t { - Normal = 0, - Import, - Native, -}; - -enum class function_access_t : std::uint8_t { - Public = 0, - Private, -}; - -/** - * @brief IR function. - * - * Consists of a name and blocks. - * @see block - */ -class function { -public: - using value_type = std::shared_ptr; /**< Value type. */ -public: - /** - * @brief Construct a new IR function. - * - * @param name Name to forward. - */ - template >> - function(StringFwd&& name, function_access_t access, std::uint32_t paramCount, function_t type = function_t::Normal) - : m_name(std::forward(name)), m_access(access), m_paramCount(paramCount), m_type(type) {} -public: - /** - * @brief Returns this function's name. - * - * @return The name. - */ - const std::string& name() const { return m_name; } - - /** - * @brief Returns the function's access. - * - * @return The access. - */ - function_access_t access() const { return m_access; } - - /** - * @brief Returns this function's parameter count. - * - * @return The parameter count. - */ - std::uint32_t param_count() const { return m_paramCount; } - - /** - * @brief Returns this function's type. - * - * @return The type. - */ - function_t type() const { return m_type; } - - /** - * @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; - function_access_t m_access; - function_t m_type; - std::uint32_t m_paramCount; - std::vector m_blocks; -}; - -} // namespace ir -} // namespace furlang - -#endif // FURLANG_IR_FUNCTION_HPP diff --git a/furlang/include/furlang/ir/instruction.hpp b/furlang/include/furlang/ir/instruction.hpp deleted file mode 100644 index 61bb81f..0000000 --- a/furlang/include/furlang/ir/instruction.hpp +++ /dev/null @@ -1,767 +0,0 @@ -#ifndef FURLANG_IR_INSTRUCTION_HPP -#define FURLANG_IR_INSTRUCTION_HPP - -#include "furlang/ir/operand.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace furlang { -namespace ir { - -/** - * @brief IR instruction type. - */ -enum class instruction_t { - Alloca, /**< Unused */ - Assign, /**< Assign */ - Add, /**< Addition */ - Sub, /**< Subtraction */ - Mul, /**< Multiplication */ - Div, /**< Division */ - Mod, /**< Modulo */ - Eq, /**< Equal */ - NotEq, /**< Not equal */ - LessThan, /**< Less than */ - GreaterThan, /**< Greater than */ - LessEq, /**< Less or equal */ - GreaterEq, /**< Greater or equal */ - Pointerof, /**< Pointerof */ - Sizeof, /**< Sizeof */ - Call, /**< Call */ - Branch, /**< Branch */ - BranchCond, /**< Conditional branch */ - Return, /**< Return */ - Phi, /**< Phi function */ -}; - -static inline std::ostream& operator<<(std::ostream& os, instruction_t type) { - switch (type) { - case instruction_t::Alloca: return os << "alloca"; - case instruction_t::Assign: return os << "assign"; - case instruction_t::Add: return os << "add"; - case instruction_t::Sub: return os << "sub"; - case instruction_t::Mul: return os << "mul"; - case instruction_t::Div: return os << "div"; - case instruction_t::Mod: return os << "mod"; - case instruction_t::Eq: return os << "eq"; - case instruction_t::NotEq: return os << "notEq"; - case instruction_t::LessThan: return os << "lessThan"; - case instruction_t::GreaterThan: return os << "greaterThan"; - case instruction_t::LessEq: return os << "lessEq"; - case instruction_t::GreaterEq: return os << "greaterEq"; - case instruction_t::Pointerof: return os << "pointerof"; - case instruction_t::Sizeof: return os << "sizeof"; - case instruction_t::Call: return os << "call"; - case instruction_t::Branch: return os << "branch"; - case instruction_t::BranchCond: return os << "branchCond"; - case instruction_t::Return: return os << "return"; - case instruction_t::Phi: return os << "phi"; - } - throw std::runtime_error("unreachable"); -} - -/** - * @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: - case instruction_t::BranchCond: - case instruction_t::Return: return true; - default: return false; - } -} - -/** - * @brief IR instruction - */ -class instruction { -public: - instruction() = default; - virtual ~instruction() = default; - - /** - * @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; - - /** - * @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 sources() { return {}; } - - /** - * @brief Returns a list of source operands of this instruction. - * - * @return The list of source operands. - */ - virtual std::vector sources() const { return {}; } -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; - - /** - * @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; - - /** - * @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 whether this instruction has a destination operand. - * - * @return true - */ - bool has_destination() const override { return true; } - - /** - * @brief Returns this instruction's destination. - * - * @return The 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 sources() override { return { &m_source }; } - - /** - * @brief Returns a list of this instruction's source operands. - * - * @return The list of source operands. - */ - std::vector sources() const override { return { &m_source }; } -private: - operand m_source; - operand m_destination; -protected: - std::ostream& print(std::ostream& os) const override { return os << m_destination << " = " << m_source; } -}; - -/** - * @brief Generic unary instruction - */ -class unary_instruction final : public instruction { -public: - /** - * @brief Construct a new unary instruction. - * - * @param type Instruction type. - * @param src Source operand. - * @param dst Destination operand. - */ - unary_instruction(instruction_t type, operand&& src, operand&& dst) - : m_type(type), m_src(std::move(src)), m_dst(std::move(dst)) {} - - ~unary_instruction() override = default; - - /** - * @brief Move constructor - */ - unary_instruction(unary_instruction&&) = default; - - /** - * @brief Move constructor - */ - unary_instruction& operator=(unary_instruction&&) = default; - - unary_instruction(const unary_instruction&) = delete; - - unary_instruction& operator=(const unary_instruction&) = delete; -public: - /** - * @brief Returns this instruction's type. - * - * @return The instruction type. - */ - instruction_t type() const override { return m_type; } - - bool has_destination() const override { return true; } - - operand& destination() override { return m_dst; } - - const operand& destination() const override { return m_dst; } - - std::vector sources() override { return { &m_src }; } - - std::vector sources() const override { return { &m_src }; } - - /** - * @brief Returns this instruction's source operand. - * - * @return The operand. - */ - const operand& src() const { return m_src; } - - /** - * @brief Returns this instruction's destination operand. - * - * @return The operand. - */ - const operand& dst() const { return m_dst; } -private: - instruction_t m_type; - operand m_src; - operand m_dst; -protected: - std::ostream& print(std::ostream& os) const override { return os << m_dst << " = " << m_type << ' ' << m_src; } -}; - -/** - * @brief Generic binary instruction - */ -class binary_instruction final : public instruction { -public: - /** - * @brief Construct a new binary operation instruction. - * - * @param type Instruction type. - * @param lhs Left-hand-side operand. - * @param rhs Right-hand-side operand. - * @param dst Destination operand. - */ - binary_instruction(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_instruction() override = default; - - /** - * @brief Move constructor - */ - binary_instruction(binary_instruction&&) = default; - - /** - * @brief Move constructor - */ - binary_instruction& operator=(binary_instruction&&) = default; - - binary_instruction(const binary_instruction&) = delete; - - binary_instruction& operator=(const binary_instruction&) = delete; -public: - /** - * @brief Returns this instruction's type. - * - * @return instruction_t::BinaryOp. - */ - instruction_t type() const override { return m_type; } - - bool has_destination() const override { return true; } - - operand& destination() override { return m_dst; } - - const operand& destination() const override { return m_dst; } - - std::vector sources() override { return { &m_lhs, &m_rhs }; } - - std::vector sources() const override { return { &m_lhs, &m_rhs }; } - - /** - * @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: - instruction_t m_type; - 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 << m_dst << " = " << m_lhs << ' ' << m_type << ' ' << m_rhs; - } -}; - -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; - - /** - * @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; /**< 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; - - /** - * @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; } - - std::vector sources() override { return { &m_condition }; } - std::vector sources() const override { return { &m_condition }; } - - /** - * @brief Returns this instruction's condition operand. - * - * @return The operand. - */ - const operand& condition() const { return m_condition; } - - /** - * @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 /**< 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() = 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; - - /** - * @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; } - - std::vector sources() override { - if (m_value.has_value()) return { &*m_value }; - return {}; - } - - std::vector sources() const override { - if (m_value.has_value()) return { &*m_value }; - 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; /**< The return value operand. */ -protected: - std::ostream& print(std::ostream& os) const override { - os << "return"; - if (m_value.has_value()) os << ' ' << m_value.value(); - return os; - } -}; - -/** - * @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; } - - bool has_destination() const override { return true; } - - /** - * @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; } - - std::vector sources() const override { - std::vector srcs; - srcs.reserve(m_labels.size()); - for (const auto& [op, _block] : m_labels) - srcs.push_back(&op); - return srcs; - } - - /** - * @brief Returns this instruction's labels. - * - * @return The labels. - */ - std::vector>& labels() { return m_labels; } - - /** - * @brief Returns this instruction's labels. - * - * @return The labels. - */ - const std::vector>& labels() const { return m_labels; } -private: - operand m_dst; - std::vector> m_labels; -protected: - std::ostream& print(std::ostream& os) const override { - os << m_dst << " = phi"; - bool first = true; - for (const auto& pair : m_labels) { - if (!first) os << ','; - first = false; - os << ' ' << pair.second << ": " << pair.first; - } - return os; - } -}; - -/** - * @brief Function call instruction. - */ -class call_instruction final : public instruction { -public: - template - call_instruction(NameFwd&& name, operand&& dst, ArgsFwd&& args) - : m_name(std::forward(name)), m_dst(std::move(dst)), m_args(std::forward(args)) {} - - ~call_instruction() override = default; - - call_instruction(call_instruction&&) noexcept = default; - call_instruction& operator=(call_instruction&&) noexcept = default; - call_instruction(const call_instruction&) = delete; - call_instruction& operator=(const call_instruction&) = delete; -public: - /** - * @brief Returns this instruction's type. - * - * @return instruction_t::Call. - */ - instruction_t type() const override { return instruction_t::Call; } - - bool has_destination() const override { return true; } - - /** - * @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; } - - std::vector sources() const override { - std::vector srcs; - srcs.reserve(m_args.size()); - for (const auto& op : m_args) - srcs.push_back(&op); - return srcs; - } - - /** - * @brief Returns this instruction's callee name. - * - * @return The name. - */ - const std::string& name() const { return m_name; } -private: - std::string m_name; - operand m_dst; - std::vector m_args; -protected: - std::ostream& print(std::ostream& os) const override { - os << m_dst << " = " << m_name << '('; - bool first = true; - for (std::size_t i = 0; i < m_args.size(); ++i) { - if (!first) os << ", "; - first = false; - } - return os << ')'; - } -}; - -} // namespace ir -} // namespace furlang - -#endif // FURLANG_IR_INSTRUCTION_HPP diff --git a/furlang/include/furlang/ir/module.hpp b/furlang/include/furlang/ir/module.hpp deleted file mode 100644 index 2940bb8..0000000 --- a/furlang/include/furlang/ir/module.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef FURLANG_IR_MODULE_HPP -#define FURLANG_IR_MODULE_HPP - -#include "furlang/ir/function.hpp" - -#include -#include - -namespace furlang { -namespace ir { - -/** - * @brief IR module - */ -class mod { -public: - using value_type = std::unique_ptr; /**< Value type. */ -public: - mod() = 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)...); - } - - /** - * @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; -}; - -} // namespace ir -} // namespace furlang - -#endif // FURLANG_IR_MODULE_HPP diff --git a/furlang/include/furlang/ir/operand.hpp b/furlang/include/furlang/ir/operand.hpp deleted file mode 100644 index 633afac..0000000 --- a/furlang/include/furlang/ir/operand.hpp +++ /dev/null @@ -1,290 +0,0 @@ -#ifndef FURLANG_IR_OPERAND_HPP -#define FURLANG_IR_OPERAND_HPP - -#include -#include -#include - -namespace furlang { -namespace ir { - -/** - * @brief Operand type - */ -enum class operand_t { - None, /**< None */ - Register, /**< Register */ - Variable, /**< Variable */ - Integer, /**< Integer */ - String, /**< String */ -}; - -/** - * @brief Alias to a register type. - */ -using register_t = std::uint32_t; - -/** - * @brief Register operand alias. - * @see operand_t::Register - */ -struct register_operand { - register_t reg = 0; - std::uint32_t ver = 0; - - register_operand() = default; - - 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; - } - - bool operator==(const register_operand& rhs) const { return reg == rhs.reg && ver == rhs.ver; } -}; - -/** - * @brief Variable operand alias. - * @see operand_t::Variable - */ -using variable_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() { - if (m_type == operand_t::String) { - m_value.string.~basic_string(); - } - } - - /** - * @brief Move constructor. - */ - operand(operand&& other) noexcept - : m_type(other.m_type) { - switch (m_type) { - case operand_t::None: break; - case operand_t::Register: { - m_value.reg = other.m_value.reg; - } break; - case operand_t::Variable: { - new (&m_value.variable) variable_operand(std::move(other.m_value.variable)); - } break; - case operand_t::Integer: { - m_value.integer = other.m_value.integer; - } break; - case operand_t::String: { - new (&m_value.string) string_operand(std::move(other.m_value.string)); - } break; - } - 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; - switch (m_type) { - case operand_t::None: break; - case operand_t::Register: { - m_value.reg = other.m_value.reg; - } break; - case operand_t::Variable: { - new (&m_value.variable) variable_operand(std::move(other.m_value.variable)); - } break; - case operand_t::Integer: { - m_value.integer = other.m_value.integer; - } break; - case operand_t::String: { - new (&m_value.string) string_operand(std::move(other.m_value.string)); - } break; - } - other.m_value.destroy(other.m_type); - return *this; - } - - 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_t value) { - operand operand; - operand.m_type = operand_t::Register; - operand.m_value.reg = { value, 0 }; - return operand; - } - - /** - * @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; - operand.m_value.reg = value; - 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; - operand.m_type = operand_t::Variable; - new (&operand.m_value.variable) variable_operand(std::forward(value)); - 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; - operand.m_value.integer = value; - 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; - operand.m_type = operand_t::String; - new (&operand.m_value.string) string_operand(std::forward(value)); - 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() { return m_value.reg; } - - /** - * @brief Returns this operand's register value. - * - * @return The register value. - */ - register_operand reg() const { return m_value.reg; } - - /** - * @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"; - case operand_t::Register: return os << '%' << operand.m_value.reg; - case operand_t::Variable: return os << operand.m_value.variable; - 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: - operand_t m_type = operand_t::None; - union value { - std::nullptr_t null = nullptr; - register_operand reg; - variable_operand variable; - integer_operand integer; - string_operand string; - - void destroy(operand_t type) { - switch (type) { - case operand_t::String: { - string.~basic_string(); - } break; - default: break; - } - null = nullptr; - } - - value() = default; - ~value() {} - - value(value&&) noexcept = delete; - value& operator=(value&&) noexcept = delete; - value(const value&) = delete; - value& operator=(const value&) = delete; - } m_value; -}; - -} // namespace ir -} // namespace furlang - -namespace std { - -template <> -struct hash { - std::size_t operator()(const furlang::ir::register_operand& op) const noexcept { - std::size_t h1 = std::hash{}(op.reg); - std::size_t h2 = std::hash{}(op.ver); - return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2)); - } -}; - -} // namespace std - -#endif // FURLANG_IR_OPERAND_HPP