Support procedure calls without arguments

This commit is contained in:
Eugen Wissner 2024-03-26 23:04:20 +01:00
parent f2a20c2825
commit 44e32760ca
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
13 changed files with 317 additions and 113 deletions

View File

@ -76,8 +76,9 @@ namespace elna::riscv
return reinterpret_cast<const std::byte *>(&this->representation) + sizeof(this->representation);
}
visitor::visitor(std::shared_ptr<source::writer> writer)
: writer(writer)
visitor::visitor(std::shared_ptr<source::writer> writer,
std::shared_ptr<source::symbol_table> table)
: writer(writer), table(table)
{
}
@ -87,15 +88,7 @@ namespace elna::riscv
{
auto format_string = this->writer->sink(reinterpret_cast<const std::byte *>("%c\n\0"), 4);
this->instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::sp, funct3_t::addi, x_register::sp, -8));
this->instructions.push_back(instruction(base_opcode::store)
.s(4, funct3_t::sw, x_register::sp, x_register::s0));
this->instructions.push_back(instruction(base_opcode::store)
.s(0, funct3_t::sw, x_register::sp, x_register::ra));
this->instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::s0, funct3_t::addi, x_register::sp, -8));
prologue();
this->instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::a1, funct3_t::addi, x_register::zero, 't'));
this->instructions.push_back(instruction(base_opcode::branch)
@ -114,32 +107,16 @@ namespace elna::riscv
this->instructions.push_back(instruction(base_opcode::jalr)
.i(x_register::ra, funct3_t::jalr, x_register::ra, 0));
this->instructions.push_back(instruction(base_opcode::load)
.i(x_register::s0, funct3_t::lw, x_register::sp, 4));
this->instructions.push_back(instruction(base_opcode::load)
.i(x_register::ra, funct3_t::lw, x_register::sp, 0));
this->instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::sp, funct3_t::addi, x_register::sp, 8));
this->instructions.push_back(instruction(base_opcode::jalr)
.i(x_register::zero, funct3_t::jalr, x_register::ra, 0));
epilogue(8);
this->writer->sink("writeb", reinterpret_cast<const std::byte *>(this->instructions.data()),
this->instructions.size() * sizeof(instruction));
this->instructions.clear();
}
{
auto format_string = this->writer->sink(reinterpret_cast<const std::byte *>("%d\n\0"), 4);
this->instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::sp, funct3_t::addi, x_register::sp, -8));
this->instructions.push_back(instruction(base_opcode::store)
.s(4, funct3_t::sw, x_register::sp, x_register::s0));
this->instructions.push_back(instruction(base_opcode::store)
.s(0, funct3_t::sw, x_register::sp, x_register::ra));
this->instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::s0, funct3_t::addi, x_register::sp, -8));
prologue();
this->instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::a1, funct3_t::addi, x_register::a0, 0));
@ -154,18 +131,10 @@ namespace elna::riscv
this->instructions.push_back(instruction(base_opcode::jalr)
.i(x_register::ra, funct3_t::jalr, x_register::ra, 0));
this->instructions.push_back(instruction(base_opcode::load)
.i(x_register::s0, funct3_t::lw, x_register::sp, 4));
this->instructions.push_back(instruction(base_opcode::load)
.i(x_register::ra, funct3_t::lw, x_register::sp, 0));
this->instructions.push_back(instruction(base_opcode::opImm)
.i(x_register::sp, funct3_t::addi, x_register::sp, 8));
this->instructions.push_back(instruction(base_opcode::jalr)
.i(x_register::zero, funct3_t::jalr, x_register::ra, 0));
epilogue(8);
this->writer->sink("writei", reinterpret_cast<const std::byte *>(this->instructions.data()),
this->instructions.size() * sizeof(instruction));
this->instructions.clear();
}
}
@ -186,29 +155,16 @@ namespace elna::riscv
{
}
void visitor::visit(source::procedure_definition *definition)
void visitor::prologue()
{
this->instructions.push_back(instruction(base_opcode::opImm));
this->instructions.push_back(instruction(base_opcode::store));
this->instructions.push_back(instruction(base_opcode::store));
this->instructions.push_back(instruction(base_opcode::opImm));
}
void visitor::visit(source::block *block)
void visitor::epilogue(const std::size_t stack_size)
{
this->instructions.push_back(instruction(base_opcode::opImm));
this->instructions.push_back(instruction(base_opcode::store));
this->instructions.push_back(instruction(base_opcode::store));
this->instructions.push_back(instruction(base_opcode::opImm));
table = block->table();
block->body().accept(this);
// Set the return value (0).
this->instructions.push_back(instruction(base_opcode::op)
.r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero));
// Prologue.
auto main_symbol =
std::dynamic_pointer_cast<source::procedure_info>(table->lookup("main"));
const uint stack_size = static_cast<std::uint32_t>(variable_counter * 4 + 8 + main_symbol->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[2].s(stack_size - 8, funct3_t::sw, x_register::sp, x_register::ra);
@ -225,19 +181,60 @@ namespace elna::riscv
.i(x_register::zero, funct3_t::jalr, x_register::ra, 0));
}
void visitor::visit(source::procedure_definition *definition)
{
prologue();
auto main_symbol =
std::dynamic_pointer_cast<source::procedure_info>(this->table->lookup(definition->identifier()));
this->table = main_symbol->scope();
definition->body().accept(this);
this->table = main_symbol->scope()->scope();
// Set the return value (0).
this->instructions.push_back(instruction(base_opcode::op)
.r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero));
epilogue(static_cast<std::uint32_t>(variable_counter * 4 + 8 + main_symbol->stack_size()));
this->writer->sink(definition->identifier(),
reinterpret_cast<const std::byte *>(this->instructions.data()),
this->instructions.size() * sizeof(instruction));
this->instructions.clear();
}
void visitor::visit(source::block *block)
{
block->body().accept(this);
}
void visitor::visit(source::program *program)
{
generate_intrinsics();
for (auto& definition : program->definitions())
{
definition->accept(this);
}
prologue();
visit(dynamic_cast<source::block *>(program));
auto main_symbol =
std::dynamic_pointer_cast<source::procedure_info>(this->table->lookup("main"));
program->body().accept(this);
// Set the return value (0).
this->instructions.push_back(instruction(base_opcode::op)
.r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero));
epilogue(static_cast<std::uint32_t>(variable_counter * 4 + 8 + main_symbol->stack_size()));
this->writer->sink("main", reinterpret_cast<const std::byte *>(this->instructions.data()),
this->instructions.size() * sizeof(instruction));
}
void visitor::visit(source::call_statement *statement)
{
statement->arguments().accept(this);
for (auto& argument : statement->arguments())
{
argument->accept(this);
}
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::jalr)

View File

@ -146,7 +146,8 @@ namespace elna::riscv
return -1;
}
void riscv32_elf(source::program *ast, const std::filesystem::path& out_file)
void riscv32_elf(source::program *ast, std::shared_ptr<source::symbol_table> table,
const std::filesystem::path& out_file)
{
ELFIO::elfio writer;
@ -197,7 +198,7 @@ namespace elna::riscv
ELFIO::relocation_section_accessor rela(writer, rel_sec);
auto _writer = std::make_shared<elfio_writer>(text_sec, ro_sec, syma, stra);
visitor _visitor{ _writer };
visitor _visitor{ _writer, table };
_visitor.visit(ast);
syma.arrange_local_symbols();

View File

@ -54,10 +54,11 @@ namespace elna::cli
}
return 2;
}
source::name_analysis_visitor().visit(ast.get());
auto global_scope = std::make_shared<source::symbol_table>();
source::name_analysis_visitor(global_scope).visit(ast.get());
source::type_analysis_visitor().visit(ast.get());
source::allocator_visitor().visit(ast.get());
riscv::riscv32_elf(ast.get(), out_file);
source::allocator_visitor(global_scope).visit(ast.get());
riscv::riscv32_elf(ast.get(), global_scope, out_file);
return 0;
}

View File

@ -165,13 +165,16 @@ namespace elna::riscv
void generate_intrinsics();
void relocate(std::string_view name, address_t target);
void prologue();
void epilogue(const std::size_t stack_size);
public:
std::uint32_t variable_counter = 1;
std::vector<reference> references;
std::shared_ptr<source::symbol_table> table;
visitor(std::shared_ptr<source::writer> writer);
visitor(std::shared_ptr<source::writer> writer,
std::shared_ptr<source::symbol_table> table);
virtual void visit(source::declaration *declaration) override;
virtual void visit(source::constant_definition *definition) override;

View File

@ -99,5 +99,6 @@ namespace elna::riscv
*/
std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label);
void riscv32_elf(source::program *ast, const std::filesystem::path& out_file);
void riscv32_elf(source::program *ast, std::shared_ptr<source::symbol_table> table,
const std::filesystem::path& out_file);
}

View File

@ -88,12 +88,14 @@ namespace elna::source
class declaration : public node
{
std::string m_identifier;
std::string m_type;
public:
declaration(const std::string& identifier);
declaration(const std::string& identifier, const std::string& type);
virtual void accept(parser_visitor *visitor) override;
std::string& identifier() noexcept;
std::string& type() noexcept;
};
/**
@ -124,25 +126,27 @@ namespace elna::source
class procedure_definition : public definition
{
std::unique_ptr<block> m_body;
std::vector<std::unique_ptr<declaration>> m_parameters;
public:
procedure_definition(const std::string& identifier, std::unique_ptr<block>&& body);
virtual void accept(parser_visitor *visitor) override;
block& body();
std::vector<std::unique_ptr<declaration>>& parameters() noexcept;
};
class call_statement : public statement
{
std::string m_name;
std::unique_ptr<expression> m_body;
std::vector<std::unique_ptr<expression>> m_arguments;
public:
call_statement(const std::string& name, std::unique_ptr<expression>&& body);
call_statement(const std::string& name);
virtual void accept(parser_visitor *visitor) override;
std::string& name() noexcept;
expression& arguments();
std::vector<std::unique_ptr<expression>>& arguments() noexcept;
};
class compound_statement : public statement
@ -201,7 +205,6 @@ namespace elna::source
std::unique_ptr<statement> m_body;
std::vector<std::unique_ptr<definition>> m_definitions;
std::vector<std::unique_ptr<declaration>> m_declarations;
std::shared_ptr<symbol_table> m_table;
public:
block(std::vector<std::unique_ptr<definition>>&& definitions,
@ -212,7 +215,6 @@ namespace elna::source
statement& body();
std::vector<std::unique_ptr<definition>>& definitions() noexcept;
std::vector<std::unique_ptr<declaration>>& declarations() noexcept;
std::shared_ptr<symbol_table> table();
};
class program : public block

View File

@ -8,6 +8,7 @@
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
namespace elna::source
{
@ -110,6 +111,32 @@ namespace elna::source
std::string what() const override;
};
class symbol_table;
/**
* Type representation.
*/
struct type
{
const std::size_t byte_size;
protected:
explicit type(const std::size_t byte_size);
};
/**
* Built-in type representation.
*/
struct primitive_type : public type
{
const std::string type_name;
primitive_type(const std::string& type_name, const std::size_t byte_size);
};
inline const primitive_type boolean_type{ "Boolean", 1 };
inline const primitive_type int_type{ "Int", 4 };
/**
* Generic language entity information.
*/
@ -122,6 +149,19 @@ namespace elna::source
info();
};
/**
* Type information.
*/
class type_info final : public info
{
class type m_type;
public:
explicit type_info(const class type& type);
~type_info() override;
const class type& type() const noexcept;
};
/**
* Constant information.
*/
@ -145,18 +185,36 @@ namespace elna::source
~variable_info() override;
};
/**
* Procedure parameter information.
*/
class parameter_info final : public info
{
class type m_type;
public:
explicit parameter_info(const class type& type);
~parameter_info() override;
const class type& type() const noexcept;
};
/**
* Procedure information.
*/
class procedure_info final : public info
{
std::size_t local_stack_size{ 0 };
std::shared_ptr<symbol_table> local_table;
public:
std::vector<parameter_info> parameter_infos;
explicit procedure_info(std::shared_ptr<symbol_table> outer_scope);
~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();
};
/**
@ -174,12 +232,39 @@ namespace elna::source
class symbol_table
{
std::unordered_map<std::string, std::shared_ptr<info>> entries;
std::shared_ptr<symbol_table> outer_scope;
public:
symbol_table();
/**
* Constructs a new symbol with an optional outer scope.
*
* \param scope Outer scope.
*/
explicit symbol_table(std::shared_ptr<symbol_table> scope = nullptr);
/**
* Looks for symbol in the table by name. Returns nullptr if the symbol
* can not be found.
*
* \param name Symbol name.
* \return Symbol from the table if found.
*/
std::shared_ptr<info> lookup(const std::string& name);
/**
* Registers new symbol.
*
* \param name Symbol name.
* \param entry Symbol information.
*/
void enter(const std::string& name, std::shared_ptr<info> entry);
/**
* Returns the outer scope or nullptr if the this is the global scope.
*
* \return Outer scope.
*/
std::shared_ptr<symbol_table> scope();
};
struct writer

View File

@ -6,21 +6,28 @@ namespace elna::source
{
class name_analysis_visitor final : public empty_visitor
{
std::shared_ptr<symbol_table> table;
std::shared_ptr<symbol_table> table = std::make_shared<symbol_table>();
public:
name_analysis_visitor(std::shared_ptr<symbol_table> table);
void visit(constant_definition *definition) override;
void visit(declaration *declaration) override;
void visit(block *block) override;
void visit(program *program) override;
void visit(procedure_definition *procedure) override;
};
class allocator_visitor final : public empty_visitor
{
std::ptrdiff_t offset;
std::shared_ptr<symbol_table> table;
public:
allocator_visitor(std::shared_ptr<symbol_table> table);
void visit(declaration *declaration) override;
void visit(block *block) override;
void visit(program *program) override;
void visit(procedure_definition *procedure) override;
};
class type_analysis_visitor final : public empty_visitor

View File

@ -19,7 +19,10 @@ namespace elna::source
void empty_visitor::visit(call_statement *statement)
{
statement->arguments().accept(this);
for (auto& argument : statement->arguments())
{
argument->accept(this);
}
}
void empty_visitor::visit(compound_statement *statement)
@ -87,8 +90,8 @@ namespace elna::source
{
}
declaration::declaration(const std::string& identifier)
: m_identifier(identifier)
declaration::declaration(const std::string& identifier, const std::string& type)
: m_identifier(identifier), m_type(type)
{
}
@ -102,6 +105,11 @@ namespace elna::source
return m_identifier;
}
std::string& declaration::type() noexcept
{
return m_type;
}
definition::definition(const std::string& identifier)
: m_identifier(identifier)
{
@ -142,12 +150,16 @@ namespace elna::source
return *m_body;
}
std::vector<std::unique_ptr<declaration>>& procedure_definition::parameters() noexcept
{
return m_parameters;
}
block::block(std::vector<std::unique_ptr<definition>>&& definitions,
std::vector<std::unique_ptr<declaration>>&& declarations,
std::unique_ptr<statement>&& body)
: m_definitions(std::move(definitions)),
m_declarations(std::move(declarations)), m_body(std::move(body)),
m_table(std::make_shared<symbol_table>())
m_declarations(std::move(declarations)), m_body(std::move(body))
{
}
@ -171,11 +183,6 @@ namespace elna::source
return m_declarations;
}
std::shared_ptr<symbol_table> block::table()
{
return m_table;
}
program::program(std::vector<std::unique_ptr<definition>>&& definitions,
std::vector<std::unique_ptr<declaration>>&& declarations,
std::unique_ptr<statement>&& body)
@ -276,8 +283,8 @@ namespace elna::source
return m_operator;
}
call_statement::call_statement(const std::string& name, std::unique_ptr<expression>&& body)
: m_name(name), m_body(std::move(body))
call_statement::call_statement(const std::string& name)
: m_name(name)
{
}
@ -291,9 +298,9 @@ namespace elna::source
return m_name;
}
expression& call_statement::arguments()
std::vector<std::unique_ptr<expression>>& call_statement::arguments() noexcept
{
return *m_body;
return m_arguments;
}
compound_statement::compound_statement(std::vector<std::unique_ptr<statement>>&& statements)
@ -537,7 +544,8 @@ namespace elna::source
{
return nullptr;
}
return std::make_unique<declaration>(declaration_identifier.value().get().identifier());
return std::make_unique<declaration>(declaration_identifier.value().get().identifier(),
type_identifier.value().get().identifier());
}
std::unique_ptr<statement> parser::parse_statement()
@ -573,11 +581,27 @@ namespace elna::source
{
return nullptr;
}
auto bang_body = parse_expression();
auto call = std::make_unique<call_statement>(function_name->get().identifier());
std::unique_ptr<expression> argument_expression;
if (bang_body != nullptr && iterator.skip(token::type::right_paren))
if (iterator.current(token::type::right_paren))
{
return std::make_unique<call_statement>(function_name->get().identifier(), std::move(bang_body));
++iterator;
return call;
}
while ((argument_expression = parse_expression()) != nullptr)
{
call->arguments().push_back(std::move(argument_expression));
if (iterator.current(token::type::right_paren))
{
++iterator;
return call;
}
if (!iterator.skip(token::type::comma))
{
break;
}
}
return nullptr;
}

View File

@ -27,23 +27,39 @@ namespace elna::source
return "Name '" + name + "' was already defined";
}
symbol_table::symbol_table()
type::type(const std::size_t byte_size)
: byte_size(byte_size)
{
}
primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size)
: type(byte_size), type_name(type_name)
{
}
symbol_table::symbol_table(std::shared_ptr<symbol_table> scope)
: outer_scope(scope)
{
if (scope == nullptr)
{
enter("writei", std::make_shared<intrinsic_info>());
enter("writeb", std::make_shared<intrinsic_info>());
}
}
std::shared_ptr<info> symbol_table::lookup(const std::string& name)
{
auto entry = entries.find(name);
if (entry == entries.cend())
{
return nullptr;
}
else
if (entry != entries.cend())
{
return entry->second;
}
if (this->outer_scope != nullptr)
{
return this->outer_scope->lookup(name);
}
return nullptr;
}
void symbol_table::enter(const std::string& name, std::shared_ptr<info> entry)
@ -51,6 +67,11 @@ namespace elna::source
entries.insert_or_assign(name, entry);
}
std::shared_ptr<symbol_table> symbol_table::scope()
{
return this->outer_scope;
}
info::~info()
{
}
@ -59,6 +80,20 @@ namespace elna::source
{
}
type_info::type_info(const class type& type)
: info(), m_type(type)
{
}
type_info::~type_info()
{
}
const class type& type_info::type() const noexcept
{
return m_type;
}
constant_info::constant_info(const std::int32_t value)
: m_value(value)
{
@ -77,6 +112,25 @@ namespace elna::source
{
}
parameter_info::parameter_info(const class type& type)
: m_type(type)
{
}
parameter_info::~parameter_info()
{
}
const class type& parameter_info::type() const noexcept
{
return m_type;
}
procedure_info::procedure_info(std::shared_ptr<symbol_table> outer_scope)
: local_table(std::make_shared<symbol_table>(outer_scope))
{
}
procedure_info::~procedure_info()
{
}
@ -91,6 +145,11 @@ namespace elna::source
this->local_stack_size = size;
}
std::shared_ptr<symbol_table> procedure_info::scope()
{
return local_table;
}
intrinsic_info::~intrinsic_info()
{
}

View File

@ -3,6 +3,11 @@
namespace elna::source
{
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table)
: table(table)
{
}
void name_analysis_visitor::visit(constant_definition *definition)
{
this->table->enter(definition->identifier(),
@ -15,12 +20,24 @@ namespace elna::source
std::make_shared<variable_info>(variable_info()));
}
void name_analysis_visitor::visit(block *block)
void name_analysis_visitor::visit(program *program)
{
this->table->enter("main", std::make_shared<procedure_info>(this->table));
empty_visitor::visit(program);
}
void name_analysis_visitor::visit(procedure_definition *procedure)
{
auto info = std::make_shared<procedure_info>(this->table);
this->table->enter(procedure->identifier(), info);
this->table = info->scope();
empty_visitor::visit(procedure);
this->table = info->scope()->scope();
}
allocator_visitor::allocator_visitor(std::shared_ptr<symbol_table> table)
: table(table)
{
this->table = block->table();
empty_visitor::visit(block);
this->table->enter("main",
std::make_shared<procedure_info>());
}
void allocator_visitor::visit(declaration *declaration)
@ -28,11 +45,19 @@ namespace elna::source
this->offset -= sizeof(std::int32_t);
}
void allocator_visitor::visit(block *block)
void allocator_visitor::visit(program *program)
{
this->offset = 0;
empty_visitor::visit(block);
std::dynamic_pointer_cast<procedure_info>(block->table()->lookup("main"))
empty_visitor::visit(program);
std::dynamic_pointer_cast<procedure_info>(table->lookup("main"))
->stack_size(std::abs(this->offset));
}
void allocator_visitor::visit(procedure_definition *procedure)
{
this->offset = 0;
empty_visitor::visit(procedure);
std::dynamic_pointer_cast<procedure_info>(table->lookup(procedure->identifier()))
->stack_size(std::abs(this->offset));
}
}

View File

@ -1 +1 @@
2
5

View File

@ -1,6 +1,5 @@
proc f;
writei(5);
proc f; writei(5);
begin
writei(2)
f()
end.