feat(IR): improve IR functions

Add access specifiers and parameter counts to furlang's IR functions.
This commit is contained in:
2026-06-28 13:16:31 +02:00
parent 7b609f87c9
commit 8466902281
5 changed files with 135 additions and 34 deletions
+32 -13
View File
@@ -1,6 +1,9 @@
#include "furc/back/furvm.hpp"
#include "furlang/ir/function.hpp"
#include "furlang/ir/instruction.hpp"
#include "furvm/function.hpp"
#include "furvm/fwd.hpp"
#include <furvm/instruction.hpp>
#include <stdexcept>
@@ -18,22 +21,38 @@ furvm::mod furvm_generator::generate(furlang::ir::mod& mod) {
}
void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::function& function) {
auto func = mod.emplace_function(function.name(), mod.bytecode().size());
func.dispatch();
switch (function.type()) {
case furlang::ir::function_t::Normal: {
if (function.access() == furlang::ir::function_access_t::Public)
mod.emplace_function(function.name(), function.param_count(), mod.bytecode().size()).dispatch();
else
mod.emplace_function_private(function.name(), function.param_count(), mod.bytecode().size()).dispatch();
function_context ctx;
for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) {
if (auto it = ctx.incompleteJumps.find(idx); it != ctx.incompleteJumps.end()) {
for (std::size_t offset : it->second) {
mod.bytecode()[offset] = mod.bytecode().size() - offset - 1;
function_context ctx;
for (furlang::ir::block_index idx = 0; idx < function.blocks().size(); ++idx) {
if (auto it = ctx.incompleteJumps.find(idx); it != ctx.incompleteJumps.end()) {
for (std::size_t offset : it->second) {
mod.bytecode()[offset] = mod.bytecode().size() - offset - 1;
}
ctx.incompleteJumps.erase(it);
}
ctx.incompleteJumps.erase(it);
}
ctx.blockOffsets[idx] = mod.bytecode().size();
for (const auto& instr : function.blocks()[idx]->instructions())
generate_instruction(mod, ctx, *instr);
generate_instruction(mod, ctx, *function.blocks()[idx]->exit());
ctx.blockOffsets[idx] = mod.bytecode().size();
for (const auto& instr : function.blocks()[idx]->instructions())
generate_instruction(mod, ctx, *instr);
generate_instruction(mod, ctx, *function.blocks()[idx]->exit());
}
} break;
case furlang::ir::function_t::Import: {
throw std::runtime_error("unimplemented");
// mod.emplace_function_private(function.name(), function.param_count(), mod.bytecode().size()).dispatch();
} break;
case furlang::ir::function_t::Native: {
if (function.access() == furlang::ir::function_access_t::Public)
mod.emplace_function(function.name(), function.param_count(), function.name()).dispatch();
else
mod.emplace_function_private(function.name(), function.param_count(), function.name()).dispatch();
} break;
}
}
+31 -7
View File
@@ -4,10 +4,14 @@
#include "furc/ast/expression.hpp" // IWYU pragma: keep
#include "furc/ast/literal.hpp" // IWYU pragma: keep
#include "furc/ast/statement.hpp" // IWYU pragma: keep
#include "furlang/ir/function.hpp"
#include "furlang/ir/instruction.hpp"
#include "furlang/ir/operand.hpp"
#include <cassert>
#include <memory>
#include <stdexcept>
#include <vector>
namespace furc::front {
@@ -16,7 +20,11 @@ namespace ir = furlang::ir;
}
void ir_generator::visit(const ast::function_definition_node& funcDef) {
m_currentFunction = std::make_unique<furlang::ir::function>(std::string(funcDef.name()));
furlang::ir::function_access_t access = (funcDef.access() == ast::declaration_access_t::Public)
? furlang::ir::function_access_t::Public
: furlang::ir::function_access_t::Private;
m_currentFunction = std::make_unique<furlang::ir::function>(std::string(funcDef.name()), access, 0);
push_block();
for (const auto& stmt : funcDef.body().statements) {
@@ -28,6 +36,21 @@ void ir_generator::visit(const ast::function_definition_node& funcDef) {
m_module.push(std::move(m_currentFunction));
}
void ir_generator::visit(const ast::function_declaration_node& funcDecl) {
if (funcDecl.type() == ast::function_declaration_node_t::Normal) return;
furlang::ir::function_t type = funcDecl.type() == ast::function_declaration_node_t::Import
? furlang::ir::function_t::Import
: furlang::ir::function_t::Native;
furlang::ir::function_access_t access = (funcDecl.access() == ast::declaration_access_t::Public)
? furlang::ir::function_access_t::Public
: furlang::ir::function_access_t::Private;
m_module.push(
std::make_unique<furlang::ir::function>(std::string(funcDecl.name()), access, funcDecl.params().size(), type));
}
void ir_generator::visit(const ast::return_statement_node& returnStmt) {
if (returnStmt.value().has_value()) {
returnStmt.value().value()->accept(*this);
@@ -149,12 +172,7 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
assert(node.lhs()->expression_type() == ast::expression_node_t::VarRead);
auto lhs = std::dynamic_pointer_cast<ast::var_read_expression_node>(node.lhs());
ir_register reg = 0;
if (auto it = m_variables.find(lhs->get_name()); it != m_variables.end()) {
reg = it->second;
} else {
m_variables[lhs->get_name()] = reg = m_registerCounter++;
}
ir_register reg = m_registerCounter++;
auto compound = node.compound();
if (compound != ast::binop_expression_node_t::None) {
@@ -165,6 +183,12 @@ void ir_generator::visit(const ast::var_assign_expression_node& node) {
} else {
push<ir::assign_instruction>(ir::operand::new_reg(rhs), ir::operand::new_reg(reg));
}
if (auto it = m_variables.find(lhs->get_name()); it != m_variables.end()) {
push<ir::assign_instruction>(ir::operand::new_reg(reg), ir::operand::new_reg(it->second));
} else {
m_variables[lhs->get_name()] = reg;
}
}
void ir_generator::visit(const ast::function_call_expression_node& node) {
+4
View File
@@ -546,6 +546,10 @@ static void adce_stage(function_context& ctx) {
if (instr->has_destination() && instr->destination().type() == furlang::ir::operand_t::Register) {
defMap[instr->destination().reg()] = instr.get();
}
if (instr->type() == furlang::ir::instruction_t::Call) {
// TODO: Check if the function has side effects
if (alive.insert(instr.get()).second) worklist.push(instr.get());
}
}
auto* exit = block->exit().get();
+26 -2
View File
@@ -8,12 +8,13 @@
#include "furlang/arena.hpp"
#include <fstream>
#include <furvm/furvm.hpp>
#include <iostream>
int main(void) {
try {
std::string programStr = R"(
native func print(value: int32);
private native func print(value: int32);
func main() -> int32 {
x = 0;
@@ -22,6 +23,7 @@ int main(void) {
while (x < y) {
x = x + z;
}
print(x);
}
)";
furlang::arena arena{};
@@ -58,8 +60,30 @@ int main(void) {
}
}
auto context = std::make_shared<furvm::context>();
auto furvmMod = context->emplace_module("main", furc::back::furvm_generator::generate(mod));
std::ofstream file("./a.fmod", std::ios::binary);
furc::back::furvm_generator::generate(mod).serialize(file);
furvmMod->serialize(file);
file.close();
furvmMod->set_native_function("print", [](furvm::executor& executor) {
furvm::thing_h thing = executor.load_thing(0);
switch (thing->type()) {
case furvm::thing_t::Int32: {
std::cout << thing->int32() << '\n';
} break;
}
});
furvm::executor_h executor = context->emplace_executor(context);
executor->push_frame(furvmMod, *furvmMod->function_at("main"));
std::cout << "--- Interpreting:\n";
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
executor->step();
}
return 0;
} catch (...) {