Support multiple function arguments

This commit is contained in:
Eugen Wissner 2024-03-29 11:01:19 +01:00
parent 07521f2b58
commit e04a816024
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
8 changed files with 137 additions and 50 deletions

View File

@ -1,4 +1,5 @@
#include "elna/backend/riscv.hpp" #include "elna/backend/riscv.hpp"
#include <cassert>
#include <memory> #include <memory>
namespace elna::riscv namespace elna::riscv
@ -168,15 +169,15 @@ namespace elna::riscv
void visitor::epilogue(const std::size_t stack_size) void visitor::epilogue(const std::size_t stack_size)
{ {
this->instructions[0].i(x_register::sp, funct3_t::addi, x_register::sp, -stack_size); this->instructions[0].i(x_register::sp, funct3_t::addi, x_register::sp, -stack_size);
this->instructions[1].s(stack_size - 4, funct3_t::sw, x_register::sp, x_register::s0); this->instructions[1].s(0, funct3_t::sw, x_register::sp, x_register::s0);
this->instructions[2].s(stack_size - 8, funct3_t::sw, x_register::sp, x_register::ra); this->instructions[2].s(4, funct3_t::sw, x_register::sp, x_register::ra);
this->instructions[3].i(x_register::s0, funct3_t::addi, x_register::sp, stack_size); this->instructions[3].i(x_register::s0, funct3_t::addi, x_register::sp, stack_size);
// Epilogue. // Epilogue.
this->instructions.push_back(instruction(base_opcode::load) this->instructions.push_back(instruction(base_opcode::load)
.i(x_register::s0, funct3_t::lw, x_register::sp, stack_size - 4)); .i(x_register::s0, funct3_t::lw, x_register::sp, 0));
this->instructions.push_back(instruction(base_opcode::load) this->instructions.push_back(instruction(base_opcode::load)
.i(x_register::ra, funct3_t::lw, x_register::sp, stack_size - 8)); .i(x_register::ra, funct3_t::lw, x_register::sp, 4));
this->instructions.push_back(instruction(base_opcode::opImm) this->instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::sp, funct3_t::addi, x_register::sp, stack_size)); .i(x_register::sp, funct3_t::addi, x_register::sp, stack_size));
this->instructions.push_back(instruction(base_opcode::jalr) this->instructions.push_back(instruction(base_opcode::jalr)
@ -226,16 +227,20 @@ namespace elna::riscv
this->instructions.push_back(instruction(base_opcode::op) this->instructions.push_back(instruction(base_opcode::op)
.r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero)); .r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero));
epilogue(static_cast<std::uint32_t>(this->variable_counter * 4 + 8 + main_symbol->stack_size())); epilogue(static_cast<std::uint32_t>(this->variable_counter * 4 + 8 + main_symbol->local_stack_size));
this->writer->sink("main", reinterpret_cast<const std::byte *>(this->instructions.data()), this->writer->sink("main", reinterpret_cast<const std::byte *>(this->instructions.data()),
this->instructions.size() * sizeof(instruction)); this->instructions.size() * sizeof(instruction));
} }
void visitor::visit(source::call_statement *statement) void visitor::visit(source::call_statement *statement)
{ {
std::size_t argument_offset{ 0 };
for (auto& argument : statement->arguments()) for (auto& argument : statement->arguments())
{ {
argument->accept(this); argument->accept(this);
this->instructions.push_back(instruction(base_opcode::store)
.s(argument_offset, funct3_t::sw, x_register::sp, x_register::a0));
argument_offset += 4;
} }
relocate(statement->name(), address_t::text); relocate(statement->name(), address_t::text);
this->instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); this->instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0));
@ -297,10 +302,17 @@ namespace elna::riscv
else if (auto variable_symbol = std::dynamic_pointer_cast<source::variable_info>(symbol)) else if (auto variable_symbol = std::dynamic_pointer_cast<source::variable_info>(symbol))
{ {
this->instructions.push_back( this->instructions.push_back(
instruction(base_opcode::store) instruction(base_opcode::load)
.i(free_register, funct3_t::lw, x_register::s0, variable_symbol->offset) .i(free_register, funct3_t::lw, x_register::s0, variable_symbol->offset)
); );
} }
else if (auto parameter_symbol = std::dynamic_pointer_cast<source::parameter_info>(symbol))
{
this->instructions.push_back(
instruction(base_opcode::load)
.i(free_register, funct3_t::lw, x_register::s0, parameter_symbol->offset)
);
}
} }
void visitor::visit(source::binary_expression *expression) void visitor::visit(source::binary_expression *expression)

View File

@ -178,11 +178,16 @@ namespace elna::source
/** /**
* Variable information. * Variable information.
*/ */
struct variable_info final : public info class variable_info final : public info
{ {
class type m_type;
public:
std::ptrdiff_t offset{ 0 }; std::ptrdiff_t offset{ 0 };
explicit variable_info(const class type& type);
~variable_info() override; ~variable_info() override;
const class type& type() const noexcept;
}; };
/** /**
@ -193,37 +198,41 @@ namespace elna::source
class type m_type; class type m_type;
public: public:
std::ptrdiff_t offset{ 0 };
explicit parameter_info(const class type& type); explicit parameter_info(const class type& type);
~parameter_info() override; ~parameter_info() override;
const class type& type() const noexcept; const class type& type() const noexcept;
}; };
/**
* Intrinsic and external procedure information.
*/
class intrinsic_info : public info
{
public:
std::vector<parameter_info> parameter_infos;
~intrinsic_info() override;
std::size_t parameter_stack_size() const noexcept;
};
/** /**
* Procedure information. * Procedure information.
*/ */
class procedure_info final : public info class procedure_info final : public intrinsic_info
{ {
std::size_t local_stack_size{ 0 };
std::shared_ptr<symbol_table> local_table; std::shared_ptr<symbol_table> local_table;
public: public:
std::vector<parameter_info> parameter_infos; std::size_t local_stack_size{ 0 };
std::size_t argument_stack_size{ 0 };
explicit procedure_info(std::shared_ptr<symbol_table> outer_scope); explicit procedure_info(std::shared_ptr<symbol_table> outer_scope);
~procedure_info() override; ~procedure_info() override;
void stack_size(const std::size_t size) noexcept;
std::size_t stack_size() const noexcept;
std::shared_ptr<symbol_table> scope(); std::shared_ptr<symbol_table> scope();
}; std::size_t stack_size() const noexcept;
/**
* Intrinsic and external procedure information.
*/
class intrinsic_info final : public info
{
public:
~intrinsic_info() override;
}; };
/** /**

View File

@ -17,9 +17,14 @@ namespace elna::source
void visit(procedure_definition *procedure) override; void visit(procedure_definition *procedure) override;
}; };
/**
* Visitor which allocates registers and stack space for variables and
* parameters.
*/
class allocator_visitor final : public empty_visitor class allocator_visitor final : public empty_visitor
{ {
std::ptrdiff_t offset; std::ptrdiff_t local_offset;
std::ptrdiff_t argument_offset;
std::shared_ptr<symbol_table> table; std::shared_ptr<symbol_table> table;
public: public:
@ -28,10 +33,13 @@ namespace elna::source
void visit(declaration *declaration) override; void visit(declaration *declaration) override;
void visit(program *program) override; void visit(program *program) override;
void visit(procedure_definition *procedure) override; void visit(procedure_definition *procedure) override;
void visit(call_statement *statement) override;
}; };
/**
* This visitor performs the type checking.
*/
class type_analysis_visitor final : public empty_visitor class type_analysis_visitor final : public empty_visitor
{ {
public:
}; };
} }

View File

@ -14,6 +14,10 @@ namespace elna::source
void empty_visitor::visit(procedure_definition *definition) void empty_visitor::visit(procedure_definition *definition)
{ {
for (auto& parameter : definition->parameters())
{
parameter->accept(this);
}
definition->body().accept(this); definition->body().accept(this);
} }

View File

@ -42,8 +42,16 @@ namespace elna::source
{ {
if (scope == nullptr) if (scope == nullptr)
{ {
enter("writei", std::make_shared<intrinsic_info>()); auto writei = std::make_shared<intrinsic_info>();
enter("writeb", std::make_shared<intrinsic_info>()); writei->parameter_infos.emplace_back(int_type);
enter("writei", writei);
auto writeb = std::make_shared<intrinsic_info>();
writeb->parameter_infos.emplace_back(boolean_type);
enter("writeb", writeb);
enter("Boolean", std::make_shared<type_info>(boolean_type));
enter("Int", std::make_shared<type_info>(int_type));
} }
} }
@ -108,10 +116,20 @@ namespace elna::source
return m_value; return m_value;
} }
variable_info::variable_info(const class type& type)
: m_type(type)
{
}
variable_info::~variable_info() variable_info::~variable_info()
{ {
} }
const class type& variable_info::type() const noexcept
{
return m_type;
}
parameter_info::parameter_info(const class type& type) parameter_info::parameter_info(const class type& type)
: m_type(type) : m_type(type)
{ {
@ -126,6 +144,15 @@ namespace elna::source
return m_type; return m_type;
} }
intrinsic_info::~intrinsic_info()
{
}
std::size_t intrinsic_info::parameter_stack_size() const noexcept
{
return this->parameter_infos.size() * sizeof(std::int32_t);
}
procedure_info::procedure_info(std::shared_ptr<symbol_table> outer_scope) procedure_info::procedure_info(std::shared_ptr<symbol_table> outer_scope)
: local_table(std::make_shared<symbol_table>(outer_scope)) : local_table(std::make_shared<symbol_table>(outer_scope))
{ {
@ -135,22 +162,13 @@ namespace elna::source
{ {
} }
std::size_t procedure_info::stack_size() const noexcept
{
return this->local_stack_size;
}
void procedure_info::stack_size(const std::size_t size) noexcept
{
this->local_stack_size = size;
}
std::shared_ptr<symbol_table> procedure_info::scope() std::shared_ptr<symbol_table> procedure_info::scope()
{ {
return local_table; return local_table;
} }
intrinsic_info::~intrinsic_info() std::size_t procedure_info::stack_size() const noexcept
{ {
return local_stack_size + argument_stack_size;
} }
} }

View File

@ -16,8 +16,9 @@ namespace elna::source
void name_analysis_visitor::visit(declaration *declaration) void name_analysis_visitor::visit(declaration *declaration)
{ {
auto declaration_type = std::dynamic_pointer_cast<type_info>(table->lookup(declaration->type()));
this->table->enter(declaration->identifier(), this->table->enter(declaration->identifier(),
std::make_shared<variable_info>(variable_info())); std::make_shared<variable_info>(declaration_type->type()));
} }
void name_analysis_visitor::visit(program *program) void name_analysis_visitor::visit(program *program)
@ -31,7 +32,15 @@ namespace elna::source
auto info = std::make_shared<procedure_info>(this->table); auto info = std::make_shared<procedure_info>(this->table);
this->table->enter(procedure->identifier(), info); this->table->enter(procedure->identifier(), info);
this->table = info->scope(); this->table = info->scope();
empty_visitor::visit(procedure);
for (auto& parameter : procedure->parameters())
{
auto declaration_type = std::dynamic_pointer_cast<type_info>(table->lookup(parameter->type()));
this->table->enter(parameter->identifier(),
std::make_shared<parameter_info>(declaration_type->type()));
}
procedure->body().accept(this);
this->table = info->scope()->scope(); this->table = info->scope()->scope();
} }
@ -42,22 +51,49 @@ namespace elna::source
void allocator_visitor::visit(declaration *declaration) void allocator_visitor::visit(declaration *declaration)
{ {
this->offset -= sizeof(std::int32_t); auto declaration_info = this->table->lookup(declaration->identifier());
if (auto variable = std::dynamic_pointer_cast<variable_info>(declaration_info))
{
this->local_offset -= sizeof(std::int32_t);
variable->offset = this->local_offset;
}
else if (auto parameter = std::dynamic_pointer_cast<parameter_info>(declaration_info))
{
parameter->offset = this->argument_offset;
this->argument_offset += sizeof(std::int32_t);
}
} }
void allocator_visitor::visit(program *program) void allocator_visitor::visit(program *program)
{ {
this->offset = 0; this->local_offset = 0;
this->argument_offset = 0;
empty_visitor::visit(program); empty_visitor::visit(program);
std::dynamic_pointer_cast<procedure_info>(table->lookup("main")) std::dynamic_pointer_cast<procedure_info>(table->lookup("main"))->local_stack_size =
->stack_size(std::abs(this->offset)); std::abs(this->local_offset);
} }
void allocator_visitor::visit(procedure_definition *procedure) void allocator_visitor::visit(procedure_definition *procedure)
{ {
this->offset = 0; this->local_offset = 0;
this->argument_offset = 0;
auto info = std::dynamic_pointer_cast<procedure_info>(this->table->lookup(procedure->identifier()));
this->table = info->scope();
empty_visitor::visit(procedure); empty_visitor::visit(procedure);
std::dynamic_pointer_cast<procedure_info>(table->lookup(procedure->identifier()))
->stack_size(std::abs(this->offset)); this->table = info->scope()->scope();
info->local_stack_size = std::abs(this->local_offset);
info->argument_stack_size = this->argument_offset;
}
void allocator_visitor::visit(call_statement *statement)
{
auto call_info = std::dynamic_pointer_cast<intrinsic_info>(this->table->lookup(statement->name()));
this->argument_offset = std::max(static_cast<std::size_t>(this->argument_offset),
call_info->parameter_stack_size());
} }
} }

View File

@ -1,2 +1,2 @@
t
5 5
3

View File

@ -1,9 +1,9 @@
proc g() proc g(a: Boolean, b: Int)
begin begin
writei(5); writeb(a);
writei(3) writei(b)
end; end;
begin begin
g() g(True, 5)
end. end.