From f48fae487877bcd1842aba3e3b6e334156cceb08 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 18 Apr 2024 12:15:26 +0200 Subject: [PATCH] Generate code for loops --- CMakeLists.txt | 1 + TODO | 7 +- backend/riscv.cpp | 571 +++++++++++++++--------------- backend/target.cpp | 12 +- cli/cl.cpp | 7 +- include/elna/backend/riscv.hpp | 40 +-- include/elna/backend/target.hpp | 9 +- include/elna/source/optimizer.hpp | 104 ++++++ include/elna/source/parser.hpp | 52 +++ include/elna/source/result.hpp | 5 +- source/optimizer.cpp | 267 ++++++++++++++ source/parser.cpp | 44 +++ tests/print_in_loop.eln | 2 +- 13 files changed, 788 insertions(+), 333 deletions(-) create mode 100644 include/elna/source/optimizer.hpp create mode 100644 source/optimizer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fb582fa..0322664 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(elna cli/main.cpp source/parser.cpp include/elna/source/parser.hpp source/result.cpp include/elna/source/result.hpp source/semantic.cpp include/elna/source/semantic.hpp + source/optimizer.cpp include/elna/source/optimizer.hpp backend/riscv.cpp include/elna/backend/riscv.hpp backend/target.cpp include/elna/backend/target.hpp cli/cl.cpp include/elna/cli/cl.hpp diff --git a/TODO b/TODO index 9a85cbe..d87a6d0 100644 --- a/TODO +++ b/TODO @@ -3,19 +3,17 @@ - Catch exceptions thrown by the argument parser and print them normally. - Structs or records. - Unions. -- While loop. - Type checking. -- Calculate additional stack space needed for subexpressions in the allocator - visitor and not in the backend. - Support immediates greater than 12 bits. - It seems instructions are correctly encoded only if the compiler is running on a little endian architecture. - Assignment to a pointer. - Static array. - Syscalls. +- Error message with an empty file wrongly says that a ")" is expected. +- Support any expressions for constants. # Shell -- Persist the history. - Replace hard coded ANSI codes with constants or functions. ## Completion @@ -25,4 +23,3 @@ - Don't hardcode fzf binary path. - Send the word under the cursor to fzf as initial input. - Home directory expansion. -- Show files in the PATH if starting at the beginning of the prompt diff --git a/backend/riscv.cpp b/backend/riscv.cpp index 66df852..e6584cb 100644 --- a/backend/riscv.cpp +++ b/backend/riscv.cpp @@ -1,5 +1,6 @@ #include "elna/backend/riscv.hpp" #include +#include #include namespace elna::riscv @@ -77,366 +78,378 @@ namespace elna::riscv return reinterpret_cast(&this->representation) + sizeof(this->representation); } - visitor::visitor(std::shared_ptr writer, - std::shared_ptr table) - : writer(writer), table(table) + static void relocate(std::string_view name, address_t target, std::vector& references, + std::vector& instructions, std::shared_ptr> writer) { + references.push_back(reference()); + references.back().name = name; + references.back().offset = writer->size() + instructions.size() * 4; + references.back().target = target; } - void visitor::generate_intrinsics() + static void prologue(std::vector& instructions) { - this->writer->sink("printf"); - { - auto format_string = this->writer->sink(reinterpret_cast("%c\n\0"), 4); - - 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) - .b(8, funct3_t::bne, x_register::zero, x_register::a0)); - this->instructions.push_back(instruction(base_opcode::opImm) - .i(x_register::a1, funct3_t::addi, x_register::zero, 'f')); - - relocate(format_string, address_t::high20); - this->instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0)); - relocate(format_string, address_t::lower12i); - this->instructions.push_back(instruction(base_opcode::opImm) - .i(x_register::a0, funct3_t::addi, x_register::a5, 0)); - - relocate("printf", address_t::text); - this->instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); - this->instructions.push_back(instruction(base_opcode::jalr) - .i(x_register::ra, 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); - - prologue(); - this->instructions.push_back(instruction(base_opcode::opImm) - .i(x_register::a1, funct3_t::addi, x_register::a0, 0)); - - relocate(format_string, address_t::high20); - this->instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0)); - relocate(format_string, address_t::lower12i); - this->instructions.push_back(instruction(base_opcode::opImm) - .i(x_register::a0, funct3_t::addi, x_register::a5, 0)); - - relocate("printf", address_t::text); - this->instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); - this->instructions.push_back(instruction(base_opcode::jalr) - .i(x_register::ra, 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(); - } - } - - void visitor::relocate(std::string_view name, address_t target) - { - this->references.push_back(reference()); - this->references.back().name = name; - this->references.back().offset = writer->size() + instructions.size() * 4; - this->references.back().target = target; - } - - void visitor::visit(source::declaration *declaration) - { - } - - void visitor::visit(source::constant_definition *definition) - { - } - - void visitor::prologue() - { - this->variable_counter = 1; - - 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)); + instructions.push_back(instruction(base_opcode::opImm)); + instructions.push_back(instruction(base_opcode::store)); + instructions.push_back(instruction(base_opcode::store)); + instructions.push_back(instruction(base_opcode::opImm)); } - void visitor::epilogue(const std::size_t stack_size) + static void epilogue(const std::size_t stack_size, std::vector& instructions) { - this->instructions[0].i(x_register::sp, funct3_t::addi, x_register::sp, -stack_size); - 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); + instructions[0].i(x_register::sp, funct3_t::addi, x_register::sp, -stack_size); + instructions[1].s(0, funct3_t::sw, x_register::sp, x_register::s0); + instructions[2].s(4, funct3_t::sw, x_register::sp, x_register::ra); + instructions[3].i(x_register::s0, funct3_t::addi, x_register::sp, stack_size); // Epilogue. - this->instructions.push_back(instruction(base_opcode::load) + instructions.push_back(instruction(base_opcode::load) .i(x_register::s0, funct3_t::lw, x_register::sp, 0)); - this->instructions.push_back(instruction(base_opcode::load) + instructions.push_back(instruction(base_opcode::load) .i(x_register::ra, funct3_t::lw, x_register::sp, 4)); - this->instructions.push_back(instruction(base_opcode::opImm) + 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) + instructions.push_back(instruction(base_opcode::jalr) .i(x_register::zero, funct3_t::jalr, x_register::ra, 0)); } - void visitor::visit(source::procedure_definition *definition) + static void generate_intrinsics(std::shared_ptr> writer, + std::vector& references) { - 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(this->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()) + writer->sink("printf"); { - definition->accept(this); + std::vector instructions; + auto format_string = writer->sink(reinterpret_cast("%c\n\0"), 4); + + prologue(instructions); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a1, funct3_t::addi, x_register::zero, 't')); + instructions.push_back(instruction(base_opcode::branch) + .b(8, funct3_t::bne, x_register::zero, x_register::a0)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a1, funct3_t::addi, x_register::zero, 'f')); + + relocate(format_string, address_t::high20, references, instructions, writer); + instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0)); + relocate(format_string, address_t::lower12i, references, instructions, writer); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a0, funct3_t::addi, x_register::a5, 0)); + + relocate("printf", address_t::text, references, instructions ,writer); + instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); + instructions.push_back(instruction(base_opcode::jalr) + .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); + + epilogue(8, instructions); + + writer->sink("writeb", reinterpret_cast(instructions.data()), + instructions.size() * sizeof(instruction)); } - prologue(); - - 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(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); - const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; - this->instructions.push_back(instruction(base_opcode::store) - .s(argument_offset, funct3_t::sw, x_register::sp, free_register)); - argument_offset += 4; - } - 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) - .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); - } + std::vector instructions; + auto format_string = writer->sink(reinterpret_cast("%d\n\0"), 4); - void visitor::visit(source::compound_statement *statement) - { - for (auto& nested_statement : statement->statements()) - { - nested_statement->accept(this); + prologue(instructions); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a1, funct3_t::addi, x_register::a0, 0)); + + relocate(format_string, address_t::high20, references, instructions, writer); + instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0)); + relocate(format_string, address_t::lower12i, references, instructions, writer); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a0, funct3_t::addi, x_register::a5, 0)); + + relocate("printf", address_t::text, references, instructions, writer); + instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); + instructions.push_back(instruction(base_opcode::jalr) + .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); + + epilogue(8, instructions); + + writer->sink("writei", reinterpret_cast(instructions.data()), + instructions.size() * sizeof(instruction)); } } - void visitor::visit(source::assign_statement *statement) + static void load_in_register(std::shared_ptr operand, const x_register target, + std::shared_ptr procedure_info, std::vector& instructions) { - const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; - auto symbol = table->lookup(statement->lvalue()); - auto variable_symbol = std::dynamic_pointer_cast(symbol); + std::shared_ptr integer_operand{ nullptr }; + std::shared_ptr variable_operand{ nullptr }; + std::shared_ptr temporary_variable{ nullptr }; + std::shared_ptr variable_symbol{ nullptr }; + std::shared_ptr parameter_symbol{ nullptr }; - statement->rvalue().accept(this); - - this->instructions.push_back(instruction(base_opcode::store) - .s(variable_symbol->offset, funct3_t::sw, x_register::s0, x_register::a0)); - } - - void visitor::visit(source::if_statement *statement) - { - statement->prerequisite().accept(this); - - const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; - - auto before_branch = instructions.size(); - instructions.push_back(instruction(base_opcode::branch)); - statement->body().accept(this); - instructions[before_branch] - .b((instructions.size() - before_branch) * 4, funct3_t::beq, x_register::zero, free_register); - } - - void visitor::visit(source::while_statement *statement) - { - statement->prerequisite().accept(this); - statement->body().accept(this); - } - - void visitor::visit(source::type_expression *type) - { - } - - void visitor::visit(source::variable_expression *variable) - { - const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; - - auto symbol = table->lookup(variable->name()); - if (auto constant_symbol = std::dynamic_pointer_cast(symbol)) + if ((integer_operand = std::dynamic_pointer_cast(operand)) != nullptr) { - this->instructions.push_back( - instruction(base_opcode::opImm) // movl $x, %eax; where $x is a number. - .i(free_register, funct3_t::addi, x_register::zero, constant_symbol->value()) - ); + instructions.push_back(instruction(base_opcode::opImm) + .i(target, funct3_t::addi, x_register::zero, integer_operand->value())); } - else if (auto variable_symbol = std::dynamic_pointer_cast(symbol)) + else if ((variable_operand = std::dynamic_pointer_cast(operand)) != nullptr) { - this->instructions.push_back( - instruction(base_opcode::load) - .i(free_register, funct3_t::lw, x_register::s0, variable_symbol->offset) - ); + const auto& name = procedure_info->scope()->lookup(variable_operand->name()); + if ((variable_symbol = std::dynamic_pointer_cast(name)) != nullptr) + { + instructions.push_back(instruction(base_opcode::load) + .i(target, funct3_t::lw, x_register::s0, variable_symbol->offset)); + } + else if ((parameter_symbol = std::dynamic_pointer_cast(name)) != nullptr) + { + instructions.push_back(instruction(base_opcode::load) + .i(target, funct3_t::lw, x_register::s0, parameter_symbol->offset)); + } } - else if (auto parameter_symbol = std::dynamic_pointer_cast(symbol)) + else if ((temporary_variable = std::dynamic_pointer_cast(operand)) != nullptr) { - this->instructions.push_back( - instruction(base_opcode::load) - .i(free_register, funct3_t::lw, x_register::s0, parameter_symbol->offset) - ); + instructions.push_back(instruction(base_opcode::load)); + instructions.back().i(target, funct3_t::lw, x_register::s0, + procedure_info->local_stack_size + 4 * temporary_variable->counter()); } } - void visitor::visit(source::binary_expression *expression) + static void store_from_register(std::shared_ptr destination, const x_register target, + std::shared_ptr procedure_info, std::vector& instructions) { - const auto lhs_register = this->register_in_use ? x_register::a0 : x_register::t0; + std::shared_ptr variable_operand{ nullptr }; + std::shared_ptr temporary_variable{ nullptr }; + std::shared_ptr variable_symbol{ nullptr }; - this->register_in_use = true; - expression->lhs().accept(this); - auto lhs_stack_position = this->variable_counter * 4; - ++this->variable_counter; + if ((variable_operand = std::dynamic_pointer_cast(destination)) != nullptr) + { + variable_symbol = std::dynamic_pointer_cast( + procedure_info->scope()->lookup(variable_operand->name())); + instructions.push_back(instruction(base_opcode::store) + .s(variable_symbol->offset, funct3_t::sw, x_register::s0, target)); + } + else if ((temporary_variable = std::dynamic_pointer_cast(destination)) != nullptr) + { + instructions.push_back(instruction(base_opcode::store)); + instructions.back().s(procedure_info->local_stack_size + 4 * temporary_variable->counter(), + funct3_t::sw, x_register::s0, target); + } + } - this->instructions.push_back( - instruction(base_opcode::store) - .s(static_cast(lhs_stack_position), funct3_t::sw, x_register::sp, x_register::a0) - ); + static void perform_binary_operation(const source::binary_operator operation, std::shared_ptr lhs, + std::shared_ptr rhs, std::shared_ptr destination, + std::shared_ptr procedure_info, std::vector& instructions) + { + constexpr auto lhs_register = x_register::a0; + std::shared_ptr variable_operand{ nullptr }; + std::shared_ptr temporary_variable{ nullptr }; + std::shared_ptr variable_symbol{ nullptr }; - this->register_in_use = false; - expression->rhs().accept(this); - this->register_in_use = lhs_register == x_register::a0; // Restore. - - this->instructions.push_back(instruction(base_opcode::load) - .i(x_register::a0, funct3_t::lw, x_register::sp, - static_cast(lhs_stack_position)) - ); + load_in_register(lhs, x_register::a0, procedure_info, instructions); + load_in_register(rhs, x_register::t0, procedure_info, instructions); // Calculate the result and assign it to a variable on the stack. - switch (expression->operation()) + switch (operation) { case source::binary_operator::sum: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::add, x_register::a0, x_register::t0)); break; case source::binary_operator::subtraction: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub)); break; case source::binary_operator::multiplication: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::mul, x_register::a0, x_register::t0, funct7_t::muldiv)); break; case source::binary_operator::division: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::div, x_register::a0, x_register::t0, funct7_t::muldiv)); break; case source::binary_operator::equals: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub)); - this->instructions.push_back(instruction(base_opcode::opImm) + instructions.push_back(instruction(base_opcode::opImm) .i(lhs_register, funct3_t::sltiu, lhs_register, 1)); break; case source::binary_operator::not_equals: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub)); - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::sltu, x_register::zero, lhs_register)); break; case source::binary_operator::less: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::sltu, x_register::a0, x_register::t0)); break; case source::binary_operator::greater_equal: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::sltu, x_register::t0, x_register::a0)); break; case source::binary_operator::greater: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::slt, x_register::a0, x_register::t0)); - this->instructions.push_back(instruction(base_opcode::opImm) + instructions.push_back(instruction(base_opcode::opImm) .i(lhs_register, funct3_t::xori, lhs_register, 1)); break; case source::binary_operator::less_equal: - this->instructions.push_back(instruction(base_opcode::op) + instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::slt, x_register::t0, x_register::a0)); - this->instructions.push_back(instruction(base_opcode::opImm) + instructions.push_back(instruction(base_opcode::opImm) .i(lhs_register, funct3_t::xori, lhs_register, 1)); break; } + store_from_register(destination, lhs_register, procedure_info, instructions); } - void visitor::visit(source::unary_expression *expression) + std::vector generate(source::intermediate_code_generator generator, + std::shared_ptr table, std::shared_ptr> writer) { - switch (expression->operation()) + std::vector references; + + generate_intrinsics(writer, references); + + for (auto& [identifier, intermediate_code] : generator) { - case source::unary_operator::dereference: - expression->operand().accept(this); + std::vector instructions; + auto main_symbol = std::dynamic_pointer_cast(table->lookup(identifier)); + std::size_t argument_offset{ 0 }; + const auto stack_size = static_cast( + intermediate_code.variable_counter() * 4 + 8 + main_symbol->stack_size()); + std::unordered_map> missing_labels; + std::unordered_map>::iterator missing_label = + missing_labels.end(); + std::unordered_map local_labels; - this->instructions.push_back(instruction(base_opcode::load) - .i(x_register::a0, funct3_t::lw, x_register::a0, 0)); - break; - case source::unary_operator::reference: - const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; - auto operand_identifier = dynamic_cast(expression->operand()).name(); - auto variable_symbol = - std::dynamic_pointer_cast(this->table->lookup(operand_identifier)); + for (auto& quadruple : intermediate_code) + { + switch (quadruple.operation()) + { + case source::quadruple_operator::start: + prologue(instructions); + break; + case source::quadruple_operator::stop: + epilogue(stack_size, instructions); + break; + case source::quadruple_operator::add: + perform_binary_operation(source::binary_operator::sum, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::sub: + perform_binary_operation(source::binary_operator::subtraction, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::mul: + perform_binary_operation(source::binary_operator::multiplication, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::div: + perform_binary_operation(source::binary_operator::division, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::eq: + perform_binary_operation(source::binary_operator::equals, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::neq: + perform_binary_operation(source::binary_operator::not_equals, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::lt: + perform_binary_operation(source::binary_operator::less, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::ge: + perform_binary_operation(source::binary_operator::greater_equal, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::gt: + perform_binary_operation(source::binary_operator::greater, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::le: + perform_binary_operation(source::binary_operator::less_equal, quadruple.operand1(), + quadruple.operand2(), quadruple.operand3(), main_symbol, instructions); + break; + case source::quadruple_operator::load: + { + auto operand_identifier = + std::dynamic_pointer_cast(quadruple.operand1())->name(); + auto variable_symbol = std::dynamic_pointer_cast( + main_symbol->scope()->lookup(operand_identifier)); - this->instructions.push_back(instruction(base_opcode::opImm) - .i(free_register, funct3_t::addi, x_register::s0, variable_symbol->offset)); - break; + load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions); + instructions.push_back(instruction(base_opcode::load) + .i(x_register::a0, funct3_t::lw, x_register::a0, 0)); + store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions); + } + break; + case source::quadruple_operator::ref: + { + auto operand_identifier = + std::dynamic_pointer_cast(quadruple.operand1())->name(); + auto variable_symbol = std::dynamic_pointer_cast( + main_symbol->scope()->lookup(operand_identifier)); + + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a0, funct3_t::addi, x_register::s0, variable_symbol->offset)); + store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions); + } + break; + case source::quadruple_operator::beqz: + load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions); + instructions.push_back(instruction(base_opcode::branch)); + missing_labels.emplace( + std::dynamic_pointer_cast(quadruple.operand3())->counter(), + [before_branch = instructions.size() - 1, &instructions](std::size_t target) { + instructions[before_branch].b((target - before_branch) * 4, + funct3_t::beq, x_register::zero, x_register::a0); + }); + break; + case source::quadruple_operator::j: + { + auto local_label = local_labels.find( + std::dynamic_pointer_cast(quadruple.operand3())->counter()); + if (local_label != local_labels.end()) + { + auto offset = -(instructions.size() - local_label->second) * 4; + instructions.push_back(instruction(base_opcode::auipc).u(x_register::a0, 0)); + instructions.push_back(instruction(base_opcode::jalr) + .i(x_register::zero, funct3_t::jalr, x_register::a0, offset)); + } + } + break; + case source::quadruple_operator::label: + { + auto label_counter = + std::dynamic_pointer_cast(quadruple.operand3())->counter(); + missing_label = missing_labels.find(label_counter); + + if (missing_label != missing_labels.end()) + { + missing_label->second(instructions.size()); + } + local_labels[label_counter] = instructions.size(); + } + break; + case source::quadruple_operator::assign: + load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions); + store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions); + break; + case source::quadruple_operator::param: + load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions); + instructions.push_back(instruction(base_opcode::store) + .s(argument_offset, funct3_t::sw, x_register::sp, x_register::a0)); + argument_offset += 4; + break; + case source::quadruple_operator::call: + relocate(std::dynamic_pointer_cast(quadruple.operand1())->name(), + address_t::text, references, instructions, writer); + instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); + instructions.push_back(instruction(base_opcode::jalr) + .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); + argument_offset = 0; + break; + } + } + writer->sink(identifier, + reinterpret_cast(instructions.data()), + instructions.size() * sizeof(instruction)); } - } - - void visitor::visit(source::integer_literal *number) - { - const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; - - this->instructions.push_back( - instruction(base_opcode::opImm) // movl $x, %eax; where $x is a number. - .i(free_register, funct3_t::addi, x_register::zero, number->number()) - ); - } - - void visitor::visit(source::boolean_literal *number) - { - const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; - - this->instructions.push_back( - instruction(base_opcode::opImm) // movl $x, %eax; where $x is a number. - .i(free_register, funct3_t::addi, x_register::zero, number->boolean()) - ); + return references; } } diff --git a/backend/target.cpp b/backend/target.cpp index b53092a..f52163d 100644 --- a/backend/target.cpp +++ b/backend/target.cpp @@ -146,8 +146,8 @@ namespace elna::riscv return -1; } - void riscv32_elf(source::program *ast, std::shared_ptr table, - const std::filesystem::path& out_file) + void riscv32_elf(source::program *ast, source::intermediate_code_generator intermediate_code_generator, + std::shared_ptr table, const std::filesystem::path& out_file) { ELFIO::elfio writer; @@ -198,12 +198,14 @@ 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, table }; - _visitor.visit(ast); + // visitor _visitor{ _writer, table }; + // _visitor.visit(ast); + auto references = generate(intermediate_code_generator, table, _writer); syma.arrange_local_symbols(); - for (auto& reference : _visitor.references) + for (auto& reference : references) + // for (auto& reference : _visitor.references) { ELFIO::Elf_Word relocated_symbol = lookup(syma, reference.name); diff --git a/cli/cl.cpp b/cli/cl.cpp index 6e6ad28..97b4d4b 100644 --- a/cli/cl.cpp +++ b/cli/cl.cpp @@ -1,6 +1,7 @@ #include "elna/cli/cl.hpp" #include "elna/backend/target.hpp" #include "elna/source/semantic.hpp" +#include "elna/source/optimizer.hpp" #include namespace elna::cli @@ -34,7 +35,11 @@ namespace elna::cli source::name_analysis_visitor(global_scope).visit(ast.get()); source::type_analysis_visitor().visit(ast.get()); source::allocator_visitor(global_scope).visit(ast.get()); - riscv::riscv32_elf(ast.get(), global_scope, out_file); + + source::intermediate_code_generator intermediate_code_generator{ global_scope }; + intermediate_code_generator.visit(ast.get()); + + riscv::riscv32_elf(ast.get(), intermediate_code_generator, global_scope, out_file); } catch (std::ios_base::failure&) { diff --git a/include/elna/backend/riscv.hpp b/include/elna/backend/riscv.hpp index c55ea81..cb5a68b 100644 --- a/include/elna/backend/riscv.hpp +++ b/include/elna/backend/riscv.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include "elna/source/parser.hpp" +#include "elna/source/optimizer.hpp" namespace elna::riscv { @@ -158,40 +158,6 @@ namespace elna::riscv std::uint32_t representation{ 0 }; }; - class visitor final : public source::parser_visitor - { - std::shared_ptr writer; - std::vector instructions; - bool register_in_use{ true }; - - 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, - std::shared_ptr table); - - virtual void visit(source::declaration *declaration) override; - virtual void visit(source::constant_definition *definition) override; - virtual void visit(source::procedure_definition *definition) override; - virtual void visit(source::call_statement *statement) override; - virtual void visit(source::compound_statement *statement) override; - virtual void visit(source::assign_statement *statement) override; - virtual void visit(source::if_statement *statement) override; - virtual void visit(source::while_statement *statement) override; - virtual void visit(source::block *block) override; - virtual void visit(source::program *program) override; - virtual void visit(source::type_expression *variable) override; - virtual void visit(source::variable_expression *variable) override; - virtual void visit(source::binary_expression *expression) override; - virtual void visit(source::unary_expression *expression) override; - virtual void visit(source::integer_literal *number) override; - virtual void visit(source::boolean_literal *number) override; - }; + std::vector generate(source::intermediate_code_generator generator, + std::shared_ptr table, std::shared_ptr> writer); } diff --git a/include/elna/backend/target.hpp b/include/elna/backend/target.hpp index 86f8bc9..2b3a1af 100644 --- a/include/elna/backend/target.hpp +++ b/include/elna/backend/target.hpp @@ -1,4 +1,7 @@ +#pragma once + #include "elna/source/parser.hpp" +#include "elna/source/optimizer.hpp" #include #include @@ -72,7 +75,7 @@ namespace elna::riscv ELFIO::section *m_section; }; - class elfio_writer final : public source::writer + class elfio_writer final : public source::writer { ELFIO::section *text; elfio_section_writer read_only; @@ -99,6 +102,6 @@ namespace elna::riscv */ std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label); - void riscv32_elf(source::program *ast, std::shared_ptr table, - const std::filesystem::path& out_file); + void riscv32_elf(source::program *ast, source::intermediate_code_generator intermediate_code_generator, + std::shared_ptr table, const std::filesystem::path& out_file); } diff --git a/include/elna/source/optimizer.hpp b/include/elna/source/optimizer.hpp new file mode 100644 index 0000000..3763e85 --- /dev/null +++ b/include/elna/source/optimizer.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include "elna/source/parser.hpp" + +namespace elna::source +{ + enum class quadruple_operator + { + start, + stop, + add, + sub, + mul, + div, + eq, + neq, + lt, + ge, + gt, + le, + load, + ref, + beqz, + j, + label, + assign, + param, + call + }; + + /** + * Single instruction representation. + */ + struct quadruple + { + quadruple(const quadruple_operator operation, std::shared_ptr operand1 = nullptr, + std::shared_ptr operand2 = nullptr, std::shared_ptr operand3 = nullptr); + + quadruple_operator operation() const noexcept; + std::shared_ptr operand1(); + std::shared_ptr operand2(); + std::shared_ptr operand3(); + + private: + quadruple_operator m_operation; + std::shared_ptr m_operand1; + std::shared_ptr m_operand2; + std::shared_ptr m_operand3; + }; + + class intermediate_code final + { + std::vector instructions; + std::uint32_t m_variable_counter; + std::uint32_t m_label_counter; + + public: + intermediate_code(); + + void emplace_back(const quadruple_operator operation, std::shared_ptr operand1 = nullptr, + std::shared_ptr operand2 = nullptr, std::shared_ptr operand3 = nullptr); + void clear(); + + std::vector::iterator begin(); + std::vector::iterator end(); + + std::int32_t variable_counter() const noexcept; + std::int32_t increment_variable() noexcept; + std::int32_t label_counter() const noexcept; + std::int32_t increment_label() noexcept; + }; + + class intermediate_code_generator final : public empty_visitor + { + std::unordered_map code; + intermediate_code current; + std::shared_ptr table; + + quadruple_operator convert(const binary_operator operation) const; + quadruple_operator convert(const unary_operator operation) const; + + public: + intermediate_code_generator(std::shared_ptr table); + + std::unordered_map::iterator begin(); + std::unordered_map::iterator end(); + + void visit(declaration *declaration) override; + void visit(constant_definition *definition) override; + void visit(procedure_definition *definition) override; + void visit(call_statement *statement) override; + void visit(assign_statement *statement) override; + void visit(if_statement *statement) override; + void visit(while_statement *statement) override; + void visit(block *block) override; + void visit(program *program) override; + void visit(type_expression *variable) override; + void visit(variable_expression *variable) override; + void visit(binary_expression *expression) override; + void visit(unary_expression *expression) override; + void visit(integer_literal *number) override; + void visit(boolean_literal *number) override; + }; +} diff --git a/include/elna/source/parser.hpp b/include/elna/source/parser.hpp index 298cb8b..29c901c 100644 --- a/include/elna/source/parser.hpp +++ b/include/elna/source/parser.hpp @@ -89,6 +89,55 @@ namespace elna::source virtual void visit(boolean_literal *boolean) override; }; + /** + * Operand representing a subexpression in the 3 address code. + */ + struct operand + { + public: + virtual ~operand() noexcept = 0; + }; + + struct integer_operand final : public operand + { + std::int32_t m_value; + + public: + explicit integer_operand(const std::int32_t value); + + std::int32_t value() const noexcept; + }; + + class variable_operand final : public operand + { + std::string m_name; + + public: + explicit variable_operand(const std::string& name); + + const std::string& name() const noexcept; + }; + + struct temporary_variable final : public operand + { + std::size_t m_counter; + + public: + explicit temporary_variable(const std::size_t counter); + + std::size_t counter() const noexcept; + }; + + struct label_operand final : public operand + { + std::size_t m_counter; + + public: + explicit label_operand(const std::size_t counter); + + std::size_t counter() const noexcept; + }; + /** * AST node. */ @@ -122,6 +171,9 @@ namespace elna::source class expression : public node { + public: + std::shared_ptr place; + protected: /** * \param position Source code position. diff --git a/include/elna/source/result.hpp b/include/elna/source/result.hpp index 6d0afc9..1602db4 100644 --- a/include/elna/source/result.hpp +++ b/include/elna/source/result.hpp @@ -301,6 +301,7 @@ namespace elna::source std::shared_ptr scope(); }; + template struct writer { /** @@ -312,7 +313,7 @@ namespace elna::source * * \return New size of the table. */ - virtual std::size_t sink(const std::string& label, const std::byte *data, std::size_t size) = 0; + virtual std::size_t sink(const std::string& label, const T *data, std::size_t size) = 0; /** * Writes data and returns a label under that the data can be accessed. @@ -322,7 +323,7 @@ namespace elna::source * * \return Label for the symbol. */ - virtual std::string_view sink(const std::byte *data, std::size_t size) = 0; + virtual std::string_view sink(const T *data, std::size_t size) = 0; /** * Creates an external symbol. diff --git a/source/optimizer.cpp b/source/optimizer.cpp new file mode 100644 index 0000000..1463d4d --- /dev/null +++ b/source/optimizer.cpp @@ -0,0 +1,267 @@ +#include "elna/source/optimizer.hpp" +#include + +namespace elna::source +{ + quadruple::quadruple(const quadruple_operator operation, std::shared_ptr operand1, + std::shared_ptr operand2, std::shared_ptr operand3) + : m_operation(operation), m_operand1(operand1), m_operand2(operand2), m_operand3(operand3) + { + } + + quadruple_operator quadruple::operation() const noexcept + { + return m_operation; + } + + std::shared_ptr quadruple::operand1() + { + return m_operand1; + } + + std::shared_ptr quadruple::operand2() + { + return m_operand2; + } + + std::shared_ptr quadruple::operand3() + { + return m_operand3; + } + + intermediate_code::intermediate_code() + { + clear(); + } + + void intermediate_code::emplace_back(const quadruple_operator operation, std::shared_ptr operand1, + std::shared_ptr operand2, std::shared_ptr operand3) + { + this->instructions.emplace_back(operation, operand1, operand2, operand3); + } + + void intermediate_code::clear() + { + this->instructions.clear(); + std::uint32_t variable_counter = 1; + std::uint32_t label_counter = 0; + } + + std::vector::iterator intermediate_code::begin() + { + return this->instructions.begin(); + } + + std::vector::iterator intermediate_code::end() + { + return this->instructions.end(); + } + + std::int32_t intermediate_code::variable_counter() const noexcept + { + return m_variable_counter; + } + + std::int32_t intermediate_code::increment_variable() noexcept + { + return m_variable_counter++; + } + + std::int32_t intermediate_code::label_counter() const noexcept + { + return m_label_counter; + } + + std::int32_t intermediate_code::increment_label() noexcept + { + return m_label_counter++; + } + + intermediate_code_generator::intermediate_code_generator(std::shared_ptr table) + : table(table) + { + } + + quadruple_operator intermediate_code_generator::convert(const binary_operator operation) const + { + switch (operation) + { + case binary_operator::sum: + return quadruple_operator::add; + case binary_operator::subtraction: + return quadruple_operator::sub; + case binary_operator::multiplication: + return quadruple_operator::mul; + case binary_operator::division: + return quadruple_operator::div; + case binary_operator::equals: + return quadruple_operator::eq; + case source::binary_operator::not_equals: + return quadruple_operator::neq; + case source::binary_operator::less: + return quadruple_operator::lt; + case source::binary_operator::greater_equal: + return quadruple_operator::ge; + case source::binary_operator::greater: + return quadruple_operator::gt; + case source::binary_operator::less_equal: + return quadruple_operator::le; + default: + assert(false); + } + } + + quadruple_operator intermediate_code_generator::convert(const unary_operator operation) const + { + switch (operation) + { + case unary_operator::reference: + return quadruple_operator::ref; + case unary_operator::dereference: + return quadruple_operator::load; + default: + assert(false); + } + } + + void intermediate_code_generator::visit(source::declaration *declaration) + { + } + + void intermediate_code_generator::visit(source::constant_definition *definition) + { + } + + void intermediate_code_generator::visit(procedure_definition *definition) + { + this->current.emplace_back(quadruple_operator::start); + definition->body().accept(this); + this->current.emplace_back(quadruple_operator::stop); + code[definition->identifier()] = std::move(this->current); + this->current.clear(); + } + + std::unordered_map::iterator intermediate_code_generator::begin() + { + return code.begin(); + } + + std::unordered_map::iterator intermediate_code_generator::end() + { + return code.end(); + } + + void intermediate_code_generator::visit(block *block) + { + block->body().accept(this); + } + + void intermediate_code_generator::visit(program *program) + { + for (auto& definition : program->definitions()) + { + definition->accept(this); + } + this->current.emplace_back(quadruple_operator::start); + program->body().accept(this); + this->current.emplace_back(quadruple_operator::stop); + code["main"] = std::move(this->current); + } + + void intermediate_code_generator::visit(call_statement *statement) + { + for (auto& argument : statement->arguments()) + { + argument->accept(this); + this->current.emplace_back(quadruple_operator::param, argument->place, nullptr, nullptr); + } + this->current.emplace_back(quadruple_operator::call, + std::make_shared(statement->name()), + std::make_shared(statement->arguments().size())); + } + + void intermediate_code_generator::visit(assign_statement *statement) + { + statement->rvalue().accept(this); + + this->current.emplace_back(quadruple_operator::assign, statement->rvalue().place, nullptr, + std::make_shared(statement->lvalue())); + } + + void intermediate_code_generator::visit(if_statement *statement) + { + statement->prerequisite().accept(this); + + auto end_label = std::make_shared(this->current.increment_label()); + this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label); + + statement->body().accept(this); + this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label); + } + + void intermediate_code_generator::visit(while_statement *statement) + { + auto condition_label = std::make_shared(this->current.increment_label()); + + this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, condition_label); + statement->prerequisite().accept(this); + + auto end_label = std::make_shared(this->current.increment_label()); + this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label); + + statement->body().accept(this); + this->current.emplace_back(quadruple_operator::j, nullptr, nullptr, condition_label); + this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label); + } + + void intermediate_code_generator::visit(type_expression *type) + { + } + + void intermediate_code_generator::visit(variable_expression *variable) + { + auto symbol = table->lookup(variable->name()); + if (auto constant_symbol = std::dynamic_pointer_cast(symbol)) + { + variable->place = std::make_shared(constant_symbol->value()); + } + else + { + variable->place = std::make_shared(variable->name()); + } + } + + void intermediate_code_generator::visit(binary_expression *expression) + { + expression->lhs().accept(this); + auto left = expression->lhs().place; + + expression->rhs().accept(this); + auto right = expression->rhs().place; + auto operation = convert(expression->operation()); + auto new_place = std::make_shared(this->current.increment_variable()); + + this->current.emplace_back(operation, left, right, new_place); + expression->place = new_place; + } + + void intermediate_code_generator::visit(unary_expression *expression) + { + expression->operand().accept(this); + auto operation = convert(expression->operation()); + auto new_place = std::make_shared(this->current.increment_variable()); + + this->current.emplace_back(operation, expression->operand().place, nullptr, new_place); + expression->place = new_place; + } + + void intermediate_code_generator::visit(integer_literal *number) + { + number->place = std::make_shared(number->number()); + } + + void intermediate_code_generator::visit(boolean_literal *number) + { + number->place = std::make_shared(number->boolean()); + } +} diff --git a/source/parser.cpp b/source/parser.cpp index ad15afa..05ac40d 100644 --- a/source/parser.cpp +++ b/source/parser.cpp @@ -99,6 +99,50 @@ namespace elna::source { } + operand::~operand() noexcept + { + } + + integer_operand::integer_operand(const std::int32_t value) + : m_value(value) + { + } + + std::int32_t integer_operand::value() const noexcept + { + return m_value; + } + + variable_operand::variable_operand(const std::string& name) + : m_name(name) + { + } + + const std::string& variable_operand::name() const noexcept + { + return m_name; + } + + temporary_variable::temporary_variable(const std::size_t counter) + : m_counter(counter) + { + } + + std::size_t temporary_variable::counter() const noexcept + { + return m_counter; + } + + label_operand::label_operand(const std::size_t counter) + : m_counter(counter) + { + } + + std::size_t label_operand::counter() const noexcept + { + return m_counter; + } + node::node(const struct position position) : source_position(position) { diff --git a/tests/print_in_loop.eln b/tests/print_in_loop.eln index e303ee6..b398e7d 100644 --- a/tests/print_in_loop.eln +++ b/tests/print_in_loop.eln @@ -2,7 +2,7 @@ var i: Int; begin i := 1; - while i <= 6 do + while i < 6 do begin writei(i); i := i + 1