Compare commits

..

3 Commits

Author SHA1 Message Date
CHatingPython 8466902281 feat(IR): improve IR functions
Add access specifiers and parameter counts to furlang's IR functions.
2026-06-28 13:21:13 +02:00
CHatingPython 7b609f87c9 feat(furc): introduce function call expression and call instruction 2026-06-28 13:21:13 +02:00
CHatingPython 0bcbe1d7d1 feat(furc): add access specifiers to functions
Introduce public and private access specifiers to functions.

Closes: #26
2026-06-28 13:21:05 +02:00
15 changed files with 439 additions and 63 deletions
+30 -4
View File
@@ -26,6 +26,16 @@ private:
std::string m_name;
};
enum class declaration_access_t {
Implicit = 0, /**< Implicit access. */
Public, /**< Public access. */
Private, /**< Private access. */
};
static inline bool same_access(declaration_access_t lhs, declaration_access_t rhs) {
return lhs == declaration_access_t::Implicit || lhs == rhs;
}
/**
* @brief Declaration node type.
*/
@@ -43,9 +53,10 @@ public:
* @brief Construct a new declaration AST node.
*
* @param location Node location.
* @param access Declaration access.
*/
declaration_node(struct location location)
: abstract_node(location) {}
declaration_node(struct location location, declaration_access_t access)
: abstract_node(location), p_access(access) {}
public:
/**
* @brief Returns this node's category.
@@ -67,8 +78,17 @@ public:
* @return The declaration type.
*/
virtual declaration_node_t declaration_type() const = 0;
/**
* @brief Returns the declaration's access.
*
* @return Access.
*/
declaration_access_t access() const { return p_access; }
protected:
bool equal(const node& rhs) const override;
protected:
declaration_access_t p_access;
};
/**
@@ -99,11 +119,12 @@ public:
*/
template <typename T, typename ParamsFwd>
function_declaration_node(struct location location,
declaration_access_t access,
T&& name,
std::optional<type>&& returnType,
ParamsFwd&& params,
function_declaration_node_t type = function_declaration_node_t::Normal)
: declaration_node(location),
: declaration_node(location, access),
p_name(std::forward<T>(name)),
p_returnType(std::move(returnType)),
p_params(std::forward<ParamsFwd>(params)),
@@ -186,11 +207,16 @@ public:
*/
template <typename T, typename ParamsFwd>
function_definition_node(struct location location,
declaration_access_t access,
T&& name,
std::optional<class type>&& type,
ParamsFwd&& params,
body&& body)
: function_declaration_node(location, std::forward<T>(name), std::move(type), std::forward<ParamsFwd>(params)),
: function_declaration_node(location,
access,
std::forward<T>(name),
std::move(type),
std::forward<ParamsFwd>(params)),
m_body(std::move(body)) {}
public:
/**
+51
View File
@@ -4,6 +4,8 @@
#include "furc/ast/node.hpp"
#include "furc/ast/statement.hpp"
#include <vector>
namespace furc {
namespace ast {
@@ -16,6 +18,7 @@ enum class expression_node_t {
Unaryop, /**< Unary operation expression */
Binop, /**< Binary operation expression */
VarAssign, /**< Variable assignment expression */
FuncCall, /**< Function call expression. */
};
/**
@@ -350,6 +353,54 @@ private:
expression_node_p m_rhs;
};
/**
* @brief Function call expression AST node.
*/
class function_call_expression_node final : public expression_node {
public:
/**
* @brief Construct a new function call expression AST node.
*
* @param location Node location.
* @param func Left-hand-side expression.
* @param args Function arguments.
*/
function_call_expression_node(struct location location,
expression_node_p&& func,
std::vector<expression_node_p>&& args)
: expression_node(location), m_func(std::move(func)), m_args(std::move(args)) {}
/**
* @brief Returns this node's left-hand-side expression.
*
* @return The left-hand-side expression.
*/
const expression_node_p& func() const { return m_func; }
/**
* @brief Returns this node's argument expressions.
*
* @return The argument expressions.
*/
const std::vector<expression_node_p>& args() const { return m_args; }
public:
/**
* @brief Returns this node's expression type.
*
* @return expression_node_t::FuncCall.
*/
expression_node_t expression_type() const override { return expression_node_t::FuncCall; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
expression_node_p m_func;
std::vector<expression_node_p> m_args;
};
} // namespace ast
} // namespace furc
+8
View File
@@ -151,6 +151,14 @@ using var_assign_expression_node_p =
using var_assign_expression_node_r =
node_r<var_assign_expression_node>; /**< Alias for var_assign_expression_node result */
class function_call_expression_node;
using function_call_expression_node_p =
node_p<function_call_expression_node>; /**< Alias for a shared pointer to function_call_expression_node. */
using function_call_expression_node_r =
node_r<function_call_expression_node>; /**< Alias for function_call_expression_node result. */
/**
* @brief List of statements.
*/
+8
View File
@@ -82,6 +82,14 @@ public:
*/
virtual void visit(const var_assign_expression_node& node) {}
/**
* @brief Visit a function_call_expression_node.
* @see function_call_expression_node
*
* @param node Node.
*/
virtual void visit(const function_call_expression_node& node) {}
/**
* @brief Visit a function_declaration_node.
* @see function_declaration_node
+2
View File
@@ -25,6 +25,7 @@ public:
furlang::ir::mod&& move_module() { return std::move(m_module); }
public:
void visit(const ast::function_definition_node& funcDef) override;
void visit(const ast::function_declaration_node& funcDecl) override;
void visit(const ast::return_statement_node& returnStmt) override;
void visit(const ast::if_statement_node& node) override;
void visit(const ast::while_statement_node& node) override;
@@ -35,6 +36,7 @@ public:
void visit(const ast::unary_op_expression_node& node) override;
void visit(const ast::binary_op_expression_node& node) override;
void visit(const ast::var_assign_expression_node& node) override;
void visit(const ast::function_call_expression_node& node) override;
private:
template <typename T, typename... Args>
void push(Args&&... args) {
+6
View File
@@ -155,6 +155,8 @@ enum class keyword_token {
While, /**< `while` */
Import, /**< `import` */
Native, /**< `native` */
Public, /**< `public` */
Private, /**< `private` */
Int32, /**< `int32` */
};
@@ -169,6 +171,8 @@ static inline std::ostream& operator<<(std::ostream& os, keyword_token keyword)
case keyword_token::While: return os << "while";
case keyword_token::Import: return os << "import";
case keyword_token::Native: return os << "native";
case keyword_token::Public: return os << "public";
case keyword_token::Private: return os << "private";
case keyword_token::Int32: return os << "int32";
}
return os;
@@ -184,6 +188,8 @@ static inline std::string operator+(const std::string& str, keyword_token keywor
case keyword_token::While: return str + "while";
case keyword_token::Import: return str + "import";
case keyword_token::Native: return str + "native";
case keyword_token::Public: return str + "public";
case keyword_token::Private: return str + "private";
case keyword_token::Int32: return str + "int32";
}
return str;
+20
View File
@@ -108,6 +108,26 @@ bool var_assign_expression_node::equal(const node& rhsNode) const {
return expression_node::equal(rhsNode) && m_compound == rhs.m_compound && m_lhs == rhs.m_lhs && m_rhs == rhs.m_rhs;
}
void function_call_expression_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& function_call_expression_node::print(std::ostream& os) const {
os << *m_func << '(';
bool first = true;
for (const auto& arg : m_args) {
if (!first) os << ", ";
first = false;
os << *arg;
}
return os << ')';
}
bool function_call_expression_node::equal(const node& rhsNode) const {
const auto& rhs = dynamic_cast<const function_call_expression_node&>(rhsNode);
return expression_node::equal(rhsNode) && m_func == rhs.m_func && m_args == rhs.m_args;
}
bool declaration_node::equal(const node& rhs) const {
return declaration_type() == dynamic_cast<const declaration_node&>(rhs).declaration_type();
}
+30 -3
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,8 +21,12 @@ 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) {
@@ -35,6 +42,18 @@ void furvm_generator::generate_function(furvm::mod& mod, const furlang::ir::func
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;
}
}
static inline furvm::instruction_t binary_op_type(furlang::ir::binary_op_instruction_t type) {
@@ -100,7 +119,15 @@ void furvm_generator::generate_instruction(furvm::mod& mod,
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::ReturnValue));
}
} break;
case furlang::ir::instruction_t::Call:
case furlang::ir::instruction_t::Call: {
const auto& call = dynamic_cast<const furlang::ir::call_instruction&>(instr);
// TODO: Implement a queue for unknown functions
furvm::function_id func = mod.get_function_id(call.name());
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::Call));
mod.bytecode().push_back((func >> 0) & 0xFF);
mod.bytecode().push_back((func >> 8) & 0xFF);
} break;
case furlang::ir::instruction_t::Alloca: throw std::runtime_error("unimplemented instruction");
case furlang::ir::instruction_t::Phi: throw std::runtime_error("unreachable");
}
+46 -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,27 @@ 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) {
std::vector<ir::operand> args;
args.reserve(node.args().size());
for (const auto& arg : node.args()) {
arg->accept(*this);
args.push_back(ir::operand::new_reg(m_registerCounter - 1));
}
if (node.func()->expression_type() != ast::expression_node_t::VarRead)
throw std::runtime_error("invalid function call left-hand-side expression");
push<ir::call_instruction>(dynamic_cast<const ast::var_read_expression_node&>(*node.func()).get_name(),
ir::operand::new_reg(m_registerCounter++),
std::move(args));
}
furlang::ir::block_index ir_generator::push_block(bool validate) {
+2
View File
@@ -102,6 +102,8 @@ token_r lexer::next_token() {
{ "while", keyword_token::While },
{ "import", keyword_token::Import },
{ "native", keyword_token::Native },
{ "public", keyword_token::Public },
{ "private", keyword_token::Private },
{ "int32", keyword_token::Int32 },
};
+70 -12
View File
@@ -9,7 +9,6 @@
#include "furc/front/token.hpp"
#include <fstream>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
@@ -54,21 +53,40 @@ ast::type_r parser::parse_type() {
ast::declaration_node_r parser::parse_declaration() {
const auto& first = peek_token();
if (first.has_error()) return ast::declaration_node_r(ast::error{ first.error().location });
switch (first->type) {
case token_t::Keyword: {
token firstToken = *first;
ast::declaration_access_t accessOverride = ast::declaration_access_t::Implicit;
switch ((*first)->keyword) {
default: break;
case keyword_token::Public:
case keyword_token::Private: {
if ((*first)->keyword == keyword_token::Public) accessOverride = ast::declaration_access_t::Public;
if ((*first)->keyword == keyword_token::Private) accessOverride = ast::declaration_access_t::Private;
auto kw = eat_token(token_t::Keyword);
if (kw.has_error()) return ast::declaration_node_r(ast::error{ kw.error().location });
firstToken = *kw;
} break;
}
ast::function_declaration_node_t funcDeclType{};
auto first = next_token();
switch ((*first)->keyword) {
auto kw = next_token();
if (kw.has_error()) return ast::declaration_node_r(ast::error{ kw.error().location });
firstToken = *kw;
switch (firstToken->keyword) {
case keyword_token::Import:
case keyword_token::Native: {
funcDeclType = ((*first)->keyword == keyword_token::Import) ? ast::function_declaration_node_t::Import
funcDeclType = (firstToken->keyword == keyword_token::Import) ? ast::function_declaration_node_t::Import
: ast::function_declaration_node_t::Native;
first = eat_token(token_t::Keyword);
if (first.has_error()) return ast::declaration_node_r(ast::error{ first.error().location });
if (first->value.keyword != keyword_token::Func)
return ast::declaration_node_r(ast::error{ first->location });
auto kw = eat_token(token_t::Keyword);
if (kw.has_error()) return ast::declaration_node_r(ast::error{ kw.error().location });
firstToken = *kw;
if (firstToken.value.keyword != keyword_token::Func)
return ast::declaration_node_r(ast::error{ firstToken.location });
}
case keyword_token::Func: {
auto name = eat_token(token_t::Identifier);
@@ -106,6 +124,12 @@ ast::declaration_node_r parser::parse_declaration() {
returnType = *type;
}
auto access = (funcDeclType == ast::function_declaration_node_t::Import)
? ast::declaration_access_t::Private
: accessOverride;
if (access == ast::declaration_access_t::Implicit) access = ast::declaration_access_t::Public;
if (!ast::same_access(accessOverride, access)) return ast::declaration_node_r(ast::error{ tok->location });
const auto& peek = peek_token();
if (peek.has_error()) return ast::declaration_node_r(ast::error{ peek.error().location });
switch (peek->type) {
@@ -114,7 +138,8 @@ ast::declaration_node_r parser::parse_declaration() {
if (body.has_error()) return ast::declaration_node_r(ast::error{ body.error().location });
if (funcDeclType != ast::function_declaration_node_t::Normal)
return ast::declaration_node_r(ast::error{ body->begin });
return m_arena->allocate_shared<ast::function_definition_node>(first->location,
return m_arena->allocate_shared<ast::function_definition_node>(firstToken.location,
access,
name->value.string,
std::move(returnType),
std::move(params),
@@ -122,7 +147,8 @@ ast::declaration_node_r parser::parse_declaration() {
}
case token_t::Semicolon: {
m_peekBuffer.clear();
return m_arena->allocate_shared<ast::function_declaration_node>(first->location,
return m_arena->allocate_shared<ast::function_declaration_node>(firstToken.location,
access,
name->value.string,
std::move(returnType),
std::move(params),
@@ -131,7 +157,7 @@ ast::declaration_node_r parser::parse_declaration() {
default: return ast::declaration_node_r(ast::error{ tok->location });
}
}
default: return ast::declaration_node_r(ast::error{ first->location });
default: return ast::declaration_node_r(ast::error{ firstToken.location });
}
}
case token_t::None:
@@ -340,6 +366,7 @@ enum class rhsop_info_t {
Unaryop,
Binop,
Assignment,
FuncCall,
};
struct rhsop_info {
@@ -352,6 +379,8 @@ struct rhsop_info {
ast::binop_expression_node_t assignment;
};
bool has_rhs() const { return type == rhsop_info_t::Binop || type == rhsop_info_t::Assignment; }
static rhsop_info create(ast::unaryop_expression_node_t type, std::uint32_t precedence) {
rhsop_info info{};
info.type = rhsop_info_t::Unaryop;
@@ -380,6 +409,14 @@ struct rhsop_info {
info.assignment = compound;
return info;
}
static rhsop_info create_function_call() {
rhsop_info info{};
info.type = rhsop_info_t::FuncCall;
info.precedence = 1;
info.associativity = associativity::Left;
return info;
}
};
ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& init, std::uint32_t precedence) {
@@ -404,6 +441,7 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
{ token_t::GreaterThan, rhsop_info::create(ast::binop_expression_node_t::GreaterThan, 9, associativity::Left) },
{ token_t::LessEq, rhsop_info::create(ast::binop_expression_node_t::LessEqual, 9, associativity::Left) },
{ token_t::GreaterEq, rhsop_info::create(ast::binop_expression_node_t::GreaterEqual, 9, associativity::Left) },
{ token_t::LParen, rhsop_info::create_function_call() },
};
ast::expression_node_p lhs = std::move(init);
@@ -416,12 +454,27 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
auto opToken = next_token();
ast::expression_node_p rhs;
if (current.type != rhsop_info_t::Unaryop) {
std::vector<ast::expression_node_p> params;
if (current.has_rhs()) {
auto expr = parse_expression_unary(current.precedence + 1); // unary prefix is always right-associative
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
rhs = std::move(expr.value());
} else if (current.type == rhsop_info_t::FuncCall && peek_token().has_value()) {
if (peek_token()->type != token_t::RParen) {
while (true) {
auto expr = parse_expression_unary(current.precedence + 1);
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
params.emplace_back(std::move(expr.value()));
if (eat_token(token_t::Comma).has_error()) break;
}
}
auto enclosing = eat_token(token_t::RParen);
if (enclosing.has_error()) return ast::expression_node_r(ast::error{ enclosing.error().location });
}
auto nextIt = s_rhsops.find(peek_token()->type);
@@ -458,6 +511,11 @@ ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& ini
std::move(lhs),
std::move(rhs));
break;
case rhsop_info_t::FuncCall:
lhs = m_arena->allocate_shared<ast::function_call_expression_node>(opToken->location,
std::move(lhs),
std::move(params));
break;
}
}
return lhs;
+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 (...) {
+41 -11
View File
@@ -3,12 +3,25 @@
#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.
*
@@ -22,18 +35,11 @@ public:
/**
* @brief Construct a new IR function.
*
* @param name Name to copy.
* @param name Name to forward.
*/
function(const std::string& name)
: m_name(name) {}
/**
* @brief Construct a new IR function.
*
* @param name Name to move.
*/
function(std::string&& name)
: m_name(std::move(name)) {}
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.
@@ -42,6 +48,27 @@ public:
*/
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.
*
@@ -57,6 +84,9 @@ public:
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;
};
@@ -7,6 +7,7 @@
#include <optional>
#include <ostream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
@@ -627,6 +628,76 @@ protected:
}
};
/**
* @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 << "call " << m_name << '(';
bool first = true;
for (const auto& op : m_args) {
if (!first) os << ", ";
first = false;
os << op;
}
return os << ") = " << m_dst;
}
};
} // namespace ir
} // namespace furlang