From e04a816024e89aab3cdbbf62ba3e42d7fb78c6c9 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 29 Mar 2024 11:01:19 +0100 Subject: [PATCH] Support multiple function arguments --- backend/riscv.cpp | 24 ++++++--- include/elna/source/result.hpp | 39 ++++++++------ include/elna/source/semantic.hpp | 12 ++++- source/parser.cpp | 4 ++ source/result.cpp | 44 ++++++++++----- source/semantic.cpp | 54 +++++++++++++++---- tests/expectations/procedure_2_statements.txt | 2 +- tests/procedure_2_statements.eln | 8 +-- 8 files changed, 137 insertions(+), 50 deletions(-) diff --git a/backend/riscv.cpp b/backend/riscv.cpp index 33ee410..7f851fd 100644 --- a/backend/riscv.cpp +++ b/backend/riscv.cpp @@ -1,4 +1,5 @@ #include "elna/backend/riscv.hpp" +#include #include namespace elna::riscv @@ -168,15 +169,15 @@ namespace elna::riscv 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[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); + this->instructions[1].s(0, funct3_t::sw, x_register::sp, x_register::s0); + 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); // Epilogue. 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) - .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) .i(x_register::sp, funct3_t::addi, x_register::sp, stack_size)); this->instructions.push_back(instruction(base_opcode::jalr) @@ -226,16 +227,20 @@ namespace elna::riscv this->instructions.push_back(instruction(base_opcode::op) .r(x_register::a0, funct3_t::_and, x_register::zero, x_register::zero)); - epilogue(static_cast(this->variable_counter * 4 + 8 + main_symbol->stack_size())); + epilogue(static_cast(this->variable_counter * 4 + 8 + main_symbol->local_stack_size)); this->writer->sink("main", reinterpret_cast(this->instructions.data()), this->instructions.size() * sizeof(instruction)); } void visitor::visit(source::call_statement *statement) { + std::size_t argument_offset{ 0 }; for (auto& argument : statement->arguments()) { 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); 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(symbol)) { this->instructions.push_back( - instruction(base_opcode::store) + instruction(base_opcode::load) .i(free_register, funct3_t::lw, x_register::s0, variable_symbol->offset) ); } + else if (auto parameter_symbol = std::dynamic_pointer_cast(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) diff --git a/include/elna/source/result.hpp b/include/elna/source/result.hpp index 8386202..ef9b34c 100644 --- a/include/elna/source/result.hpp +++ b/include/elna/source/result.hpp @@ -178,11 +178,16 @@ namespace elna::source /** * Variable information. */ - struct variable_info final : public info + class variable_info final : public info { + class type m_type; + + public: std::ptrdiff_t offset{ 0 }; + explicit variable_info(const class type& type); ~variable_info() override; + const class type& type() const noexcept; }; /** @@ -193,37 +198,41 @@ namespace elna::source class type m_type; public: + std::ptrdiff_t offset{ 0 }; + explicit parameter_info(const class type& type); ~parameter_info() override; const class type& type() const noexcept; }; + /** + * Intrinsic and external procedure information. + */ + class intrinsic_info : public info + { + public: + std::vector parameter_infos; + + ~intrinsic_info() override; + std::size_t parameter_stack_size() const noexcept; + }; + /** * 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 local_table; public: - std::vector parameter_infos; + std::size_t local_stack_size{ 0 }; + std::size_t argument_stack_size{ 0 }; explicit procedure_info(std::shared_ptr outer_scope); ~procedure_info() override; - void stack_size(const std::size_t size) noexcept; - std::size_t stack_size() const noexcept; std::shared_ptr scope(); - }; - - /** - * Intrinsic and external procedure information. - */ - class intrinsic_info final : public info - { - public: - ~intrinsic_info() override; + std::size_t stack_size() const noexcept; }; /** diff --git a/include/elna/source/semantic.hpp b/include/elna/source/semantic.hpp index 853509b..d6af82e 100644 --- a/include/elna/source/semantic.hpp +++ b/include/elna/source/semantic.hpp @@ -17,9 +17,14 @@ namespace elna::source void visit(procedure_definition *procedure) override; }; + /** + * Visitor which allocates registers and stack space for variables and + * parameters. + */ class allocator_visitor final : public empty_visitor { - std::ptrdiff_t offset; + std::ptrdiff_t local_offset; + std::ptrdiff_t argument_offset; std::shared_ptr table; public: @@ -28,10 +33,13 @@ namespace elna::source void visit(declaration *declaration) override; void visit(program *program) 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 { - public: }; } diff --git a/source/parser.cpp b/source/parser.cpp index d47e4df..28a620c 100644 --- a/source/parser.cpp +++ b/source/parser.cpp @@ -14,6 +14,10 @@ namespace elna::source void empty_visitor::visit(procedure_definition *definition) { + for (auto& parameter : definition->parameters()) + { + parameter->accept(this); + } definition->body().accept(this); } diff --git a/source/result.cpp b/source/result.cpp index 99609c5..471e4ff 100644 --- a/source/result.cpp +++ b/source/result.cpp @@ -42,8 +42,16 @@ namespace elna::source { if (scope == nullptr) { - enter("writei", std::make_shared()); - enter("writeb", std::make_shared()); + auto writei = std::make_shared(); + writei->parameter_infos.emplace_back(int_type); + enter("writei", writei); + + auto writeb = std::make_shared(); + writeb->parameter_infos.emplace_back(boolean_type); + enter("writeb", writeb); + + enter("Boolean", std::make_shared(boolean_type)); + enter("Int", std::make_shared(int_type)); } } @@ -108,10 +116,20 @@ namespace elna::source return m_value; } + variable_info::variable_info(const class type& type) + : m_type(type) + { + } + variable_info::~variable_info() { } + const class type& variable_info::type() const noexcept + { + return m_type; + } + parameter_info::parameter_info(const class type& type) : m_type(type) { @@ -126,6 +144,15 @@ namespace elna::source 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 outer_scope) : local_table(std::make_shared(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 procedure_info::scope() { return local_table; } - intrinsic_info::~intrinsic_info() + std::size_t procedure_info::stack_size() const noexcept { + return local_stack_size + argument_stack_size; } } diff --git a/source/semantic.cpp b/source/semantic.cpp index a9e3d70..1f35058 100644 --- a/source/semantic.cpp +++ b/source/semantic.cpp @@ -16,8 +16,9 @@ namespace elna::source void name_analysis_visitor::visit(declaration *declaration) { + auto declaration_type = std::dynamic_pointer_cast(table->lookup(declaration->type())); this->table->enter(declaration->identifier(), - std::make_shared(variable_info())); + std::make_shared(declaration_type->type())); } void name_analysis_visitor::visit(program *program) @@ -31,7 +32,15 @@ namespace elna::source auto info = std::make_shared(this->table); this->table->enter(procedure->identifier(), info); this->table = info->scope(); - empty_visitor::visit(procedure); + + for (auto& parameter : procedure->parameters()) + { + auto declaration_type = std::dynamic_pointer_cast(table->lookup(parameter->type())); + this->table->enter(parameter->identifier(), + std::make_shared(declaration_type->type())); + } + procedure->body().accept(this); + this->table = info->scope()->scope(); } @@ -42,22 +51,49 @@ namespace elna::source 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(declaration_info)) + { + this->local_offset -= sizeof(std::int32_t); + variable->offset = this->local_offset; + } + else if (auto parameter = std::dynamic_pointer_cast(declaration_info)) + { + parameter->offset = this->argument_offset; + this->argument_offset += sizeof(std::int32_t); + } } void allocator_visitor::visit(program *program) { - this->offset = 0; + this->local_offset = 0; + this->argument_offset = 0; + empty_visitor::visit(program); - std::dynamic_pointer_cast(table->lookup("main")) - ->stack_size(std::abs(this->offset)); + std::dynamic_pointer_cast(table->lookup("main"))->local_stack_size = + std::abs(this->local_offset); } void allocator_visitor::visit(procedure_definition *procedure) { - this->offset = 0; + this->local_offset = 0; + this->argument_offset = 0; + auto info = std::dynamic_pointer_cast(this->table->lookup(procedure->identifier())); + this->table = info->scope(); + empty_visitor::visit(procedure); - std::dynamic_pointer_cast(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(this->table->lookup(statement->name())); + + this->argument_offset = std::max(static_cast(this->argument_offset), + call_info->parameter_stack_size()); } } diff --git a/tests/expectations/procedure_2_statements.txt b/tests/expectations/procedure_2_statements.txt index 7c1a169..b087d44 100644 --- a/tests/expectations/procedure_2_statements.txt +++ b/tests/expectations/procedure_2_statements.txt @@ -1,2 +1,2 @@ +t 5 -3 diff --git a/tests/procedure_2_statements.eln b/tests/procedure_2_statements.eln index 447a9a1..5f4a07c 100644 --- a/tests/procedure_2_statements.eln +++ b/tests/procedure_2_statements.eln @@ -1,9 +1,9 @@ -proc g() +proc g(a: Boolean, b: Int) begin - writei(5); - writei(3) + writeb(a); + writei(b) end; begin - g() + g(True, 5) end.