From 44e32760ca0893dec283595efb8be67e50987ab2 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 26 Mar 2024 23:04:20 +0100 Subject: [PATCH] Support procedure calls without arguments --- backend/riscv.cpp | 113 ++++++++++---------- backend/target.cpp | 5 +- cli/cl.cpp | 7 +- include/elna/backend/riscv.hpp | 5 +- include/elna/backend/target.hpp | 3 +- include/elna/source/parser.hpp | 14 +-- include/elna/source/result.hpp | 87 ++++++++++++++- include/elna/source/semantic.hpp | 13 ++- source/parser.cpp | 60 +++++++---- source/result.cpp | 75 +++++++++++-- source/semantic.cpp | 41 +++++-- tests/expectations/procedure_definition.txt | 2 +- tests/procedure_definition.eln | 5 +- 13 files changed, 317 insertions(+), 113 deletions(-) diff --git a/backend/riscv.cpp b/backend/riscv.cpp index 2ce4b92..9bedb84 100644 --- a/backend/riscv.cpp +++ b/backend/riscv.cpp @@ -76,8 +76,9 @@ namespace elna::riscv return reinterpret_cast(&this->representation) + sizeof(this->representation); } - visitor::visitor(std::shared_ptr writer) - : writer(writer) + visitor::visitor(std::shared_ptr writer, + std::shared_ptr table) + : writer(writer), table(table) { } @@ -87,15 +88,7 @@ namespace elna::riscv { auto format_string = this->writer->sink(reinterpret_cast("%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(this->instructions.data()), this->instructions.size() * sizeof(instruction)); - this->instructions.clear(); } { auto format_string = this->writer->sink(reinterpret_cast("%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(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::visit(source::block *block) + 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)); + } - 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(table->lookup("main")); - const uint stack_size = static_cast(variable_counter * 4 + 8 + main_symbol->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[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(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(variable_counter * 4 + 8 + main_symbol->stack_size())); + this->writer->sink(definition->identifier(), + reinterpret_cast(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(program)); + auto main_symbol = + std::dynamic_pointer_cast(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(variable_counter * 4 + 8 + main_symbol->stack_size())); this->writer->sink("main", reinterpret_cast(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) diff --git a/backend/target.cpp b/backend/target.cpp index cd5375d..7586f03 100644 --- a/backend/target.cpp +++ b/backend/target.cpp @@ -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 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(text_sec, ro_sec, syma, stra); - visitor _visitor{ _writer }; + visitor _visitor{ _writer, table }; _visitor.visit(ast); syma.arrange_local_symbols(); diff --git a/cli/cl.cpp b/cli/cl.cpp index e4e9c7d..d2799c0 100644 --- a/cli/cl.cpp +++ b/cli/cl.cpp @@ -54,10 +54,11 @@ namespace elna::cli } return 2; } - source::name_analysis_visitor().visit(ast.get()); + auto global_scope = std::make_shared(); + 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; } diff --git a/include/elna/backend/riscv.hpp b/include/elna/backend/riscv.hpp index acb6ed8..bedd3e7 100644 --- a/include/elna/backend/riscv.hpp +++ b/include/elna/backend/riscv.hpp @@ -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 references; std::shared_ptr table; - visitor(std::shared_ptr writer); + visitor(std::shared_ptr writer, + std::shared_ptr table); virtual void visit(source::declaration *declaration) override; virtual void visit(source::constant_definition *definition) override; diff --git a/include/elna/backend/target.hpp b/include/elna/backend/target.hpp index 648595f..86f8bc9 100644 --- a/include/elna/backend/target.hpp +++ b/include/elna/backend/target.hpp @@ -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 table, + const std::filesystem::path& out_file); } diff --git a/include/elna/source/parser.hpp b/include/elna/source/parser.hpp index 291aa8a..fa2804d 100644 --- a/include/elna/source/parser.hpp +++ b/include/elna/source/parser.hpp @@ -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 m_body; + std::vector> m_parameters; public: procedure_definition(const std::string& identifier, std::unique_ptr&& body); virtual void accept(parser_visitor *visitor) override; block& body(); + std::vector>& parameters() noexcept; }; class call_statement : public statement { std::string m_name; - std::unique_ptr m_body; + std::vector> m_arguments; public: - call_statement(const std::string& name, std::unique_ptr&& body); + call_statement(const std::string& name); virtual void accept(parser_visitor *visitor) override; std::string& name() noexcept; - expression& arguments(); + std::vector>& arguments() noexcept; }; class compound_statement : public statement @@ -201,7 +205,6 @@ namespace elna::source std::unique_ptr m_body; std::vector> m_definitions; std::vector> m_declarations; - std::shared_ptr m_table; public: block(std::vector>&& definitions, @@ -212,7 +215,6 @@ namespace elna::source statement& body(); std::vector>& definitions() noexcept; std::vector>& declarations() noexcept; - std::shared_ptr table(); }; class program : public block diff --git a/include/elna/source/result.hpp b/include/elna/source/result.hpp index 15f79c6..8386202 100644 --- a/include/elna/source/result.hpp +++ b/include/elna/source/result.hpp @@ -8,6 +8,7 @@ #include #include #include +#include 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 local_table; public: + std::vector parameter_infos; + + 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(); }; /** @@ -174,12 +232,39 @@ namespace elna::source class symbol_table { std::unordered_map> entries; + std::shared_ptr outer_scope; public: - symbol_table(); + /** + * Constructs a new symbol with an optional outer scope. + * + * \param scope Outer scope. + */ + explicit symbol_table(std::shared_ptr 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 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 entry); + + /** + * Returns the outer scope or nullptr if the this is the global scope. + * + * \return Outer scope. + */ + std::shared_ptr scope(); }; struct writer diff --git a/include/elna/source/semantic.hpp b/include/elna/source/semantic.hpp index dd4c2ae..853509b 100644 --- a/include/elna/source/semantic.hpp +++ b/include/elna/source/semantic.hpp @@ -6,21 +6,28 @@ namespace elna::source { class name_analysis_visitor final : public empty_visitor { - std::shared_ptr table; + std::shared_ptr table = std::make_shared(); public: + name_analysis_visitor(std::shared_ptr 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 table; public: + allocator_visitor(std::shared_ptr 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 diff --git a/source/parser.cpp b/source/parser.cpp index 6d1148b..8db99c4 100644 --- a/source/parser.cpp +++ b/source/parser.cpp @@ -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>& procedure_definition::parameters() noexcept + { + return m_parameters; + } + block::block(std::vector>&& definitions, std::vector>&& declarations, std::unique_ptr&& body) : m_definitions(std::move(definitions)), - m_declarations(std::move(declarations)), m_body(std::move(body)), - m_table(std::make_shared()) + m_declarations(std::move(declarations)), m_body(std::move(body)) { } @@ -171,11 +183,6 @@ namespace elna::source return m_declarations; } - std::shared_ptr block::table() - { - return m_table; - } - program::program(std::vector>&& definitions, std::vector>&& declarations, std::unique_ptr&& body) @@ -276,8 +283,8 @@ namespace elna::source return m_operator; } - call_statement::call_statement(const std::string& name, std::unique_ptr&& 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>& call_statement::arguments() noexcept { - return *m_body; + return m_arguments; } compound_statement::compound_statement(std::vector>&& statements) @@ -537,7 +544,8 @@ namespace elna::source { return nullptr; } - return std::make_unique(declaration_identifier.value().get().identifier()); + return std::make_unique(declaration_identifier.value().get().identifier(), + type_identifier.value().get().identifier()); } std::unique_ptr parser::parse_statement() @@ -573,11 +581,27 @@ namespace elna::source { return nullptr; } - auto bang_body = parse_expression(); + auto call = std::make_unique(function_name->get().identifier()); + std::unique_ptr argument_expression; - if (bang_body != nullptr && iterator.skip(token::type::right_paren)) + if (iterator.current(token::type::right_paren)) { - return std::make_unique(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; } diff --git a/source/result.cpp b/source/result.cpp index 397233e..99609c5 100644 --- a/source/result.cpp +++ b/source/result.cpp @@ -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) { - enter("writei", std::make_shared()); - enter("writeb", std::make_shared()); + } + + 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 scope) + : outer_scope(scope) + { + if (scope == nullptr) + { + enter("writei", std::make_shared()); + enter("writeb", std::make_shared()); + } } std::shared_ptr 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 entry) @@ -51,6 +67,11 @@ namespace elna::source entries.insert_or_assign(name, entry); } + std::shared_ptr 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 outer_scope) + : local_table(std::make_shared(outer_scope)) + { + } + procedure_info::~procedure_info() { } @@ -91,6 +145,11 @@ namespace elna::source this->local_stack_size = size; } + std::shared_ptr procedure_info::scope() + { + return local_table; + } + intrinsic_info::~intrinsic_info() { } diff --git a/source/semantic.cpp b/source/semantic.cpp index 7d3660a..a9e3d70 100644 --- a/source/semantic.cpp +++ b/source/semantic.cpp @@ -3,6 +3,11 @@ namespace elna::source { + name_analysis_visitor::name_analysis_visitor(std::shared_ptr 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())); } - void name_analysis_visitor::visit(block *block) + void name_analysis_visitor::visit(program *program) + { + this->table->enter("main", std::make_shared(this->table)); + empty_visitor::visit(program); + } + + void name_analysis_visitor::visit(procedure_definition *procedure) + { + auto info = std::make_shared(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 table) + : table(table) { - this->table = block->table(); - empty_visitor::visit(block); - this->table->enter("main", - std::make_shared()); } 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(block->table()->lookup("main")) + empty_visitor::visit(program); + std::dynamic_pointer_cast(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(table->lookup(procedure->identifier())) ->stack_size(std::abs(this->offset)); } } diff --git a/tests/expectations/procedure_definition.txt b/tests/expectations/procedure_definition.txt index 0cfbf08..7ed6ff8 100644 --- a/tests/expectations/procedure_definition.txt +++ b/tests/expectations/procedure_definition.txt @@ -1 +1 @@ -2 +5 diff --git a/tests/procedure_definition.eln b/tests/procedure_definition.eln index f862f9a..94bbe85 100644 --- a/tests/procedure_definition.eln +++ b/tests/procedure_definition.eln @@ -1,6 +1,5 @@ -proc f; - writei(5); +proc f; writei(5); begin - writei(2) + f() end.