refactor(IR)!: change IR block structure
It now requires an exit instruction.
This commit is contained in:
@@ -35,6 +35,13 @@ public:
|
|||||||
void visit(const ast::binop_expression_node& node) override;
|
void visit(const ast::binop_expression_node& node) override;
|
||||||
void visit(const ast::var_assign_expression_node& node) override;
|
void visit(const ast::var_assign_expression_node& node) override;
|
||||||
private:
|
private:
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
void push(Args&&... args) {
|
||||||
|
if (!m_currentBlock->emplace<T>(std::forward<Args>(args)...)) {
|
||||||
|
throw std::runtime_error("block exited too soon");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
furlang::ir::block_index push_block();
|
furlang::ir::block_index push_block();
|
||||||
private:
|
private:
|
||||||
furlang::ir::module m_module;
|
furlang::ir::module m_module;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ void ir_generator::visit(const ast::function_definition_node& funcDef) {
|
|||||||
for (const auto& stmt : funcDef.body()->statements) {
|
for (const auto& stmt : funcDef.body()->statements) {
|
||||||
stmt->accept(*this);
|
stmt->accept(*this);
|
||||||
}
|
}
|
||||||
|
m_currentBlock->emplace<ir::return_instruction>();
|
||||||
|
|
||||||
m_module.push(std::move(m_currentFunction));
|
m_module.push(std::move(m_currentFunction));
|
||||||
}
|
}
|
||||||
@@ -35,16 +36,16 @@ void ir_generator::visit(const ast::return_statement_node& returnStmt) {
|
|||||||
|
|
||||||
if (returnStmt.value().present()) {
|
if (returnStmt.value().present()) {
|
||||||
returnStmt.value()->accept(*this);
|
returnStmt.value()->accept(*this);
|
||||||
m_currentBlock->emplace<ir::return_instruction>(ir::operand::new_reg(m_registerCounter - 1));
|
push<ir::return_instruction>(ir::operand::new_reg(m_registerCounter - 1));
|
||||||
} else {
|
} else {
|
||||||
m_currentBlock->emplace<ir::return_instruction>();
|
push<ir::return_instruction>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ir_generator::visit(const ast::if_statement_node& node) {
|
void ir_generator::visit(const ast::if_statement_node& node) {
|
||||||
node.cond()->accept(*this);
|
node.cond()->accept(*this);
|
||||||
ir_register cond = m_registerCounter - 1;
|
ir_register cond = m_registerCounter - 1;
|
||||||
m_currentBlock->emplace<ir::branch_cond_instruction>(ir::operand::new_reg(cond),
|
push<ir::branch_cond_instruction>(ir::operand::new_reg(cond),
|
||||||
m_currentFunction->blocks().size(),
|
m_currentFunction->blocks().size(),
|
||||||
m_currentFunction->blocks().size() + 1);
|
m_currentFunction->blocks().size() + 1);
|
||||||
|
|
||||||
@@ -68,18 +69,18 @@ void ir_generator::visit(const ast::compound_statement_node& node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ir_generator::visit(const ast::string_literal_node& node) {
|
void ir_generator::visit(const ast::string_literal_node& node) {
|
||||||
m_currentBlock->emplace<furlang::ir::assign_instruction>(ir::operand::new_string(std::string(*node.value())),
|
push<furlang::ir::assign_instruction>(ir::operand::new_string(std::string(*node.value())),
|
||||||
ir::operand::new_reg(m_registerCounter++));
|
ir::operand::new_reg(m_registerCounter++));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ir_generator::visit(const ast::integer_literal_node& node) {
|
void ir_generator::visit(const ast::integer_literal_node& node) {
|
||||||
m_currentBlock->emplace<furlang::ir::assign_instruction>(ir::operand::new_integer(*node.value()),
|
push<furlang::ir::assign_instruction>(ir::operand::new_integer(*node.value()),
|
||||||
ir::operand::new_reg(m_registerCounter++));
|
ir::operand::new_reg(m_registerCounter++));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ir_generator::visit(const ast::var_read_expression_node& node) {
|
void ir_generator::visit(const ast::var_read_expression_node& node) {
|
||||||
if (auto it = m_variables.find(*node.get_name()); it != m_variables.end()) {
|
if (auto it = m_variables.find(*node.get_name()); it != m_variables.end()) {
|
||||||
m_currentBlock->emplace<furlang::ir::assign_instruction>(ir::operand::new_reg(it->second),
|
push<furlang::ir::assign_instruction>(ir::operand::new_reg(it->second),
|
||||||
ir::operand::new_reg(m_registerCounter++));
|
ir::operand::new_reg(m_registerCounter++));
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("unknown variable");
|
throw std::runtime_error("unknown variable");
|
||||||
@@ -114,7 +115,7 @@ void ir_generator::visit(const ast::binop_expression_node& node) {
|
|||||||
node.rhs()->accept(*this);
|
node.rhs()->accept(*this);
|
||||||
ir_register rhs = m_registerCounter - 1;
|
ir_register rhs = m_registerCounter - 1;
|
||||||
ir_register dst = m_registerCounter++;
|
ir_register dst = m_registerCounter++;
|
||||||
m_currentBlock->emplace<furlang::ir::binary_op_instruction>(binary_op_instruction_t(node.type()),
|
push<furlang::ir::binary_op_instruction>(binary_op_instruction_t(node.type()),
|
||||||
ir::operand::new_reg(lhs),
|
ir::operand::new_reg(lhs),
|
||||||
ir::operand::new_reg(rhs),
|
ir::operand::new_reg(rhs),
|
||||||
ir::operand::new_reg(dst));
|
ir::operand::new_reg(dst));
|
||||||
@@ -135,16 +136,20 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
|
|||||||
|
|
||||||
auto compound = node.compound();
|
auto compound = node.compound();
|
||||||
if (compound != ast::binop_expression_node_t::None) {
|
if (compound != ast::binop_expression_node_t::None) {
|
||||||
m_currentBlock->emplace<ir::binary_op_instruction>(binary_op_instruction_t(compound),
|
push<ir::binary_op_instruction>(binary_op_instruction_t(compound),
|
||||||
ir::operand::new_reg(reg),
|
ir::operand::new_reg(reg),
|
||||||
ir::operand::new_reg(rhs),
|
ir::operand::new_reg(rhs),
|
||||||
ir::operand::new_reg(reg));
|
ir::operand::new_reg(reg));
|
||||||
} else {
|
} else {
|
||||||
m_currentBlock->emplace<ir::assign_instruction>(ir::operand::new_reg(rhs), ir::operand::new_reg(reg));
|
push<ir::assign_instruction>(ir::operand::new_reg(rhs), ir::operand::new_reg(reg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
furlang::ir::block_index ir_generator::push_block() {
|
furlang::ir::block_index ir_generator::push_block() {
|
||||||
|
if (!m_currentFunction->blocks().empty() && !m_currentFunction->blocks().back()->has_exit()) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"block " + std::to_string(m_currentFunction->blocks().size() - 1) + " is lacking an exit");
|
||||||
|
}
|
||||||
ir::block_index index = m_currentFunction->blocks().size();
|
ir::block_index index = m_currentFunction->blocks().size();
|
||||||
m_currentBlock = m_currentFunction->push();
|
m_currentBlock = m_currentFunction->push();
|
||||||
return index;
|
return index;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ int main(void) {
|
|||||||
for (const auto& instruction : block->instructions()) {
|
for (const auto& instruction : block->instructions()) {
|
||||||
std::cout << " " << *instruction << '\n';
|
std::cout << " " << *instruction << '\n';
|
||||||
}
|
}
|
||||||
|
std::cout << " " << *block->exit() << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,26 @@ public:
|
|||||||
block() = default;
|
block() = default;
|
||||||
public:
|
public:
|
||||||
template <typename T, typename... Args, typename = std::enable_if_t<std::is_base_of_v<instruction, T>>>
|
template <typename T, typename... Args, typename = std::enable_if_t<std::is_base_of_v<instruction, T>>>
|
||||||
void emplace(Args&&... args) {
|
bool emplace(Args&&... args) {
|
||||||
m_instructions.emplace_back(std::make_unique<T>(std::forward<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;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<value_type>& instructions() { return m_instructions; }
|
std::vector<value_type>& instructions() { return m_instructions; }
|
||||||
const std::vector<value_type>& instructions() const { return m_instructions; }
|
const std::vector<value_type>& instructions() const { return m_instructions; }
|
||||||
|
|
||||||
|
bool has_exit() const { return m_exit != nullptr; }
|
||||||
|
value_type& exit() { return m_exit; }
|
||||||
|
const value_type& exit() const { return m_exit; }
|
||||||
private:
|
private:
|
||||||
std::vector<value_type> m_instructions;
|
std::vector<value_type> m_instructions;
|
||||||
|
value_type m_exit;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
|
|||||||
@@ -20,6 +20,15 @@ enum class instruction_t {
|
|||||||
Return,
|
Return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline bool is_exit_instruction(instruction_t type) {
|
||||||
|
switch (type) {
|
||||||
|
case instruction_t::Branch:
|
||||||
|
case instruction_t::BranchCond:
|
||||||
|
case instruction_t::Return: return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class instruction {
|
class instruction {
|
||||||
public:
|
public:
|
||||||
instruction() = default;
|
instruction() = default;
|
||||||
|
|||||||
Reference in New Issue
Block a user