Support multiple function arguments
This commit is contained in:
parent
07521f2b58
commit
e04a816024
@ -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)
|
||||||
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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:
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
|
t
|
||||||
5
|
5
|
||||||
3
|
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user