feat(furc/IR): move IR from furlang to furc
First try baby, whoo!
This commit is contained in:
@@ -0,0 +1,406 @@
|
|||||||
|
#ifndef FURC_BACK_IR_HPP
|
||||||
|
#define FURC_BACK_IR_HPP
|
||||||
|
|
||||||
|
#include "furc/front/ast.hpp"
|
||||||
|
#include "furlang/arena.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <optional>
|
||||||
|
#include <stack>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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 <typename... Args, typename = std::enable_if_t<std::is_constructible_v<value_u, Args...>>>
|
||||||
|
ir_operand(type_e type, Args&&... args)
|
||||||
|
: type(type), value(std::forward<Args>(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<ir_operand> destination;
|
||||||
|
std::vector<ir_operand> sources;
|
||||||
|
|
||||||
|
ir_instruction(type_e type,
|
||||||
|
std::optional<ir_operand> destination = {},
|
||||||
|
std::initializer_list<ir_operand> 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<ir_instruction> 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<std::string, ir_variable*> 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<ir_type> params;
|
||||||
|
ir_type retType;
|
||||||
|
std::vector<ir_basic_block> 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<ir_function_variable>(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<ir_function*> 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<ir_module_variable>(type, varCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
ir_function* add_function(ir_function&& function) {
|
||||||
|
return functions.emplace_back(arena.allocate<ir_function>(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 <typename... Args, typename = std::enable_if_t<std::is_constructible_v<ir_instruction, Args...>>>
|
||||||
|
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>(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<std::ptrdiff_t>(++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<std::ptrdiff_t>(blockIdx);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ir_context& go_next() { return go(blockIdx + 1); }
|
||||||
|
ir_context& go_last() { return go(std::min<std::uint64_t>(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<ir_context> m_context;
|
||||||
|
|
||||||
|
ir_context m_initContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace furc
|
||||||
|
|
||||||
|
#endif // FURC_BACK_IR_HPP
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
#include "furc/back/ir.hpp"
|
||||||
|
|
||||||
|
#include "furc/front/ast.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
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<ir_function>(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<std::uint64_t>(node.value) } } });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furc
|
||||||
+5
-6
@@ -1,3 +1,4 @@
|
|||||||
|
#include "furc/back/ir.hpp"
|
||||||
#include "furc/front/lexer.hpp"
|
#include "furc/front/lexer.hpp"
|
||||||
#include "furc/front/parser.hpp"
|
#include "furc/front/parser.hpp"
|
||||||
#include "furlang/arena.hpp"
|
#include "furlang/arena.hpp"
|
||||||
@@ -6,17 +7,15 @@ int main(void) {
|
|||||||
furlang::arena arena;
|
furlang::arena arena;
|
||||||
|
|
||||||
std::string_view content = R"(
|
std::string_view content = R"(
|
||||||
func main(argc: u64) -> s32 pre(arc > 1) {
|
func main(argc: u64) -> s32 {
|
||||||
x: s32 = 1 + 2 * 3;
|
x: s32 = 1 + 2 * 3;
|
||||||
println(x);
|
|
||||||
return if (x == 9) 1 else 0;
|
return if (x == 9) 1 else 0;
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
furc::lexer lexer = { "<AK>", content };
|
furc::lexer lexer = { "<AK>", content };
|
||||||
furc::parser parser = { std::move(lexer), arena };
|
furc::parser parser = { std::move(lexer), arena };
|
||||||
|
furc::ir_module irModule = furc::ir_generator::generate(parser.parse());
|
||||||
auto program = parser.parse();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
#ifndef FURLANG_IR_BLOCK_HPP
|
|
||||||
#define FURLANG_IR_BLOCK_HPP
|
|
||||||
|
|
||||||
#include "furlang/ir/instruction.hpp"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
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<instruction>; /**< 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 <typename T, typename... Args, typename = std::enable_if_t<std::is_base_of_v<instruction, T>>>
|
|
||||||
bool emplace(Args&&... args) {
|
|
||||||
if (has_exit()) return false;
|
|
||||||
auto instr = std::make_unique<T>(std::forward<Args>(args)...);
|
|
||||||
if (is_exit_instruction(instr->type())) {
|
|
||||||
m_exit = std::move(instr);
|
|
||||||
} else {
|
|
||||||
m_instructions.emplace_back(std::move(instr));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns this block's instructions.
|
|
||||||
*
|
|
||||||
* @return The instructions.
|
|
||||||
*/
|
|
||||||
std::vector<value_type>& instructions() { return m_instructions; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns this block's instructions.
|
|
||||||
*
|
|
||||||
* @return The instructions.
|
|
||||||
*/
|
|
||||||
const std::vector<value_type>& 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<value_type> m_instructions;
|
|
||||||
value_type m_exit;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ir
|
|
||||||
} // namespace furlang
|
|
||||||
|
|
||||||
#endif // FURLANG_IR_BLOCK_HPP
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
#ifndef FURLANG_IR_FUNCTION_HPP
|
|
||||||
#define FURLANG_IR_FUNCTION_HPP
|
|
||||||
|
|
||||||
#include "furlang/ir/block.hpp"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
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<block>; /**< Value type. */
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Construct a new IR function.
|
|
||||||
*
|
|
||||||
* @param name Name to forward.
|
|
||||||
*/
|
|
||||||
template <typename StringFwd, typename = std::enable_if_t<std::is_constructible_v<std::string, StringFwd>>>
|
|
||||||
function(StringFwd&& name, function_access_t access, std::uint32_t paramCount, function_t type = function_t::Normal)
|
|
||||||
: m_name(std::forward<StringFwd>(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<block>()); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns this function's IR blocks.
|
|
||||||
*
|
|
||||||
* @return The IR blocks.
|
|
||||||
*/
|
|
||||||
const std::vector<value_type>& 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<value_type> m_blocks;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ir
|
|
||||||
} // namespace furlang
|
|
||||||
|
|
||||||
#endif // FURLANG_IR_FUNCTION_HPP
|
|
||||||
@@ -1,767 +0,0 @@
|
|||||||
#ifndef FURLANG_IR_INSTRUCTION_HPP
|
|
||||||
#define FURLANG_IR_INSTRUCTION_HPP
|
|
||||||
|
|
||||||
#include "furlang/ir/operand.hpp"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <optional>
|
|
||||||
#include <ostream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
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<operand*> sources() { return {}; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a list of source operands of this instruction.
|
|
||||||
*
|
|
||||||
* @return The list of source operands.
|
|
||||||
*/
|
|
||||||
virtual std::vector<const operand*> 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<operand*> sources() override { return { &m_source }; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a list of this instruction's source operands.
|
|
||||||
*
|
|
||||||
* @return The list of source operands.
|
|
||||||
*/
|
|
||||||
std::vector<const operand*> 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<operand*> sources() override { return { &m_src }; }
|
|
||||||
|
|
||||||
std::vector<const operand*> 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<operand*> sources() override { return { &m_lhs, &m_rhs }; }
|
|
||||||
|
|
||||||
std::vector<const operand*> 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<operand*> sources() override { return { &m_condition }; }
|
|
||||||
std::vector<const operand*> 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<operand*> sources() override {
|
|
||||||
if (m_value.has_value()) return { &*m_value };
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<const operand*> 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<operand>& value() const { return m_value; }
|
|
||||||
private:
|
|
||||||
std::optional<operand> 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<const operand*> sources() const override {
|
|
||||||
std::vector<const operand*> 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<std::pair<operand, block_index>>& labels() { return m_labels; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns this instruction's labels.
|
|
||||||
*
|
|
||||||
* @return The labels.
|
|
||||||
*/
|
|
||||||
const std::vector<std::pair<operand, block_index>>& labels() const { return m_labels; }
|
|
||||||
private:
|
|
||||||
operand m_dst;
|
|
||||||
std::vector<std::pair<operand, block_index>> 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 <typename NameFwd, typename ArgsFwd>
|
|
||||||
call_instruction(NameFwd&& name, operand&& dst, ArgsFwd&& args)
|
|
||||||
: m_name(std::forward<NameFwd>(name)), m_dst(std::move(dst)), m_args(std::forward<ArgsFwd>(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<const operand*> sources() const override {
|
|
||||||
std::vector<const operand*> 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<operand> 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
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#ifndef FURLANG_IR_MODULE_HPP
|
|
||||||
#define FURLANG_IR_MODULE_HPP
|
|
||||||
|
|
||||||
#include "furlang/ir/function.hpp"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace furlang {
|
|
||||||
namespace ir {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief IR module
|
|
||||||
*/
|
|
||||||
class mod {
|
|
||||||
public:
|
|
||||||
using value_type = std::unique_ptr<function>; /**< 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 <typename... Args>
|
|
||||||
value_type& push(Args&&... args) {
|
|
||||||
return m_functions.emplace_back(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns this module's functions.
|
|
||||||
*
|
|
||||||
* @return The functions.
|
|
||||||
*/
|
|
||||||
std::vector<value_type>& functions() { return m_functions; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns this module's functions.
|
|
||||||
*
|
|
||||||
* @return The functions.
|
|
||||||
*/
|
|
||||||
const std::vector<value_type>& functions() const { return m_functions; }
|
|
||||||
private:
|
|
||||||
std::vector<value_type> m_functions;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ir
|
|
||||||
} // namespace furlang
|
|
||||||
|
|
||||||
#endif // FURLANG_IR_MODULE_HPP
|
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
#ifndef FURLANG_IR_OPERAND_HPP
|
|
||||||
#define FURLANG_IR_OPERAND_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <ostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
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 <typename T>
|
|
||||||
static operand new_variable(T&& value) {
|
|
||||||
operand operand;
|
|
||||||
operand.m_type = operand_t::Variable;
|
|
||||||
new (&operand.m_value.variable) variable_operand(std::forward<T>(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 <typename T>
|
|
||||||
static operand new_string(T&& value) {
|
|
||||||
operand operand;
|
|
||||||
operand.m_type = operand_t::String;
|
|
||||||
new (&operand.m_value.string) string_operand(std::forward<T>(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<furlang::ir::register_operand> {
|
|
||||||
std::size_t operator()(const furlang::ir::register_operand& op) const noexcept {
|
|
||||||
std::size_t h1 = std::hash<decltype(op.reg)>{}(op.reg);
|
|
||||||
std::size_t h2 = std::hash<std::uint32_t>{}(op.ver);
|
|
||||||
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace std
|
|
||||||
|
|
||||||
#endif // FURLANG_IR_OPERAND_HPP
|
|
||||||
Reference in New Issue
Block a user