From 5e9b4259ca7279d36f1b7b8f1e66605a7d781f81 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 5 Feb 2025 13:24:50 +0100 Subject: [PATCH] Add multiple of the pointer target size --- CMakeLists.txt | 14 +-- Rakefile | 30 ++++- gcc/elna-generic.cc | 18 ++- gcc/elna-tree.cc | 53 +++++++-- include/elna/gcc/elna-generic.h | 1 - include/elna/gcc/elna-tree.h | 1 + rakelib/cross.rake | 99 ++-------------- rakelib/shared.rb | 67 +++++++++++ rakelib/tester.rake | 2 +- source.elna | 24 ++-- tools/init.c | 204 ++++++++++++++++++++++++++++++++ {cli => tools}/main.cc | 0 12 files changed, 380 insertions(+), 133 deletions(-) create mode 100644 tools/init.c rename {cli => tools}/main.cc (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ee0227..c0cec04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,15 +9,15 @@ find_package(Boost CONFIG COMPONENTS process program_options REQUIRED) find_package(FLEX REQUIRED) find_package(BISON REQUIRED) -FLEX_TARGET(lexer source/lexer.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.cc) -BISON_TARGET(parser source/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cc) +FLEX_TARGET(lexer boot/lexer.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.cc) +BISON_TARGET(parser boot/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cc) add_flex_bison_dependency(lexer parser) add_library(elna-frontend - source/ast.cc include/elna/source/ast.h - source/types.cc include/elna/source/types.h - source/driver.cc include/elna/source/driver.h - source/result.cc include/elna/source/result.h + boot/ast.cc include/elna/boot/ast.h + include/elna/boot/symbol.h + boot/driver.cc include/elna/boot/driver.h + boot/result.cc include/elna/boot/result.h ${BISON_parser_OUTPUTS} ${FLEX_lexer_OUTPUTS} ) target_include_directories(elna-frontend PRIVATE ${CMAKE_CURRENT_BINARY_DIR} include) @@ -25,7 +25,7 @@ target_compile_options(elna-frontend PRIVATE $<$:-fno-exceptions -fno-rtti> ) -add_executable(elna cli/main.cc) +add_executable(elna tools/main.cc) target_link_libraries(elna PRIVATE elna-frontend) target_include_directories(elna PRIVATE ${CMAKE_CURRENT_BINARY_DIR} include ${Boost_INCLUDE_DIR}) target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES}) diff --git a/Rakefile b/Rakefile index 9eaeb9d..95924ba 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,16 @@ +require 'pathname' +require 'rake/clean' +require_relative 'rakelib/shared' + +CLOBBER.include TMP + +task :default do + sh 'make -C build' + sh './build/bin/elna' +end + +desc 'Build the bootstrap compiler' +task :boot do # MacOS: # --- # CC=gcc-14 CXX=g++-14 \ @@ -8,8 +21,17 @@ # --enable-languages=c,c++,elna \ # --with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX15.2.sdk \ # --prefix=$(realpath ../gcc-install) - -task :default do - sh 'make -C build' - sh './build/bin/elna' +end + +desc 'Build cross toolchain' +task :cross, [:target] do |_, args| + args.with_defaults target: 'riscv32-unknown-linux-gnu' + + Rake::Task['cross:binutils'].invoke args[:target] + Rake::Task['cross:gcc1'].invoke args[:target] + Rake::Task['cross:headers'].invoke args[:target] + Rake::Task['cross:kernel'].invoke args[:target] + Rake::Task['cross:glibc'].invoke args[:target] + Rake::Task['cross:gcc2'].invoke args[:target] + Rake::Task['cross:init'].invoke args[:target] end diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index 193cf3e..79532e5 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -74,15 +74,7 @@ namespace gcc { auto body_type = build_type(expression->body()); - this->current_expression = build1(CONVERT_EXPR, this->symbol_map->lookup("Word"), TYPE_SIZE_UNIT(body_type)); - } - - bool generic_visitor::is_integral_type(tree type) - { - gcc_assert(TYPE_P(type)); - - return type == this->symbol_map->lookup("Int") - || type == this->symbol_map->lookup("Word"); + this->current_expression = build1(CONVERT_EXPR, this->symbol_map->lookup("Word"), size_in_bytes(body_type)); } bool generic_visitor::is_numeric_type(tree type) @@ -333,7 +325,9 @@ namespace gcc location_t expression_location = get_location(&expression->position()); - if (is_pointer_type(left_type) && is_integral_type(right_type)) + if ((is_pointer_type(left_type) || is_pointer_type(right_type)) + && (expression->operation() == boot::binary_operator::sum + || expression->operation() == boot::binary_operator::subtraction)) { this->current_expression = do_pointer_arithmetic(expression->operation(), left, right); if (this->current_expression == error_mark_node) @@ -342,6 +336,10 @@ namespace gcc "invalid operation %s on a pointer and an integral type", boot::print_binary_operator(expression->operation())); } + else if (TREE_TYPE(this->current_expression) == ssizetype) + { + this->current_expression = fold_convert(this->symbol_map->lookup("Int"), this->current_expression); + } return; } if (left_type != right_type && !are_compatible_pointers(left, right)) diff --git a/gcc/elna-tree.cc b/gcc/elna-tree.cc index fb162f3..e1d77c5 100644 --- a/gcc/elna-tree.cc +++ b/gcc/elna-tree.cc @@ -23,6 +23,12 @@ namespace gcc return TREE_CODE(type) == POINTER_TYPE; } + bool is_integral_type(tree type) + { + gcc_assert(TYPE_P(type)); + return TREE_CODE(type) == INTEGER_TYPE; + } + bool are_compatible_pointers(tree lhs, tree rhs) { return (lhs == null_pointer_node || rhs == null_pointer_node) @@ -87,19 +93,52 @@ namespace gcc tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right) { - tree result = error_mark_node; - tree convert_expression = fold_convert(sizetype, right); - if (binary_operator == boot::binary_operator::sum) { - result = fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(left), left, convert_expression); + tree pointer{ NULL_TREE }; + tree offset{ NULL_TREE }; + + if (is_pointer_type(TREE_TYPE(left)) && is_integral_type(TREE_TYPE(right))) + { + pointer = left; + offset = right; + } + else if (is_integral_type(TREE_TYPE(left)) && is_pointer_type(TREE_TYPE(right))) + { + pointer = right; + offset = left; + } + else + { + return error_mark_node; + } + tree size_exp = fold_convert(TREE_TYPE(offset), size_in_bytes(TREE_TYPE(TREE_TYPE(pointer)))); + + offset = fold_build2(MULT_EXPR, TREE_TYPE(offset), offset, size_exp); + offset = fold_convert(sizetype, offset); + + return fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(pointer), pointer, offset); } else if (binary_operator == boot::binary_operator::subtraction) { - convert_expression = fold_build1(NEGATE_EXPR, sizetype, convert_expression); - result = fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(left), left, convert_expression); + if (is_pointer_type(TREE_TYPE(left)) && is_integral_type(TREE_TYPE(right))) + { + tree pointer_type = TREE_TYPE(left); + tree offset_type = TREE_TYPE(right); + tree size_exp = fold_convert(offset_type, size_in_bytes(TREE_TYPE(pointer_type))); + + tree convert_expression = fold_build2(MULT_EXPR, offset_type, right, size_exp); + convert_expression = fold_convert(sizetype, convert_expression); + + convert_expression = fold_build1(NEGATE_EXPR, sizetype, convert_expression); + return fold_build2(POINTER_PLUS_EXPR, pointer_type, left, convert_expression); + } else if (is_pointer_type(TREE_TYPE(left)) && is_pointer_type(TREE_TYPE(right)) + && TREE_TYPE(left) == TREE_TYPE(right)) + { + return fold_build2(POINTER_DIFF_EXPR, ssizetype, left, right); + } } - return result; + return error_mark_node; } tree build_binary_operation(bool condition, boot::binary_expression *expression, diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index c93f938..2f84e0b 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -33,7 +33,6 @@ namespace gcc void make_if_branch(boot::conditional_statements& branch, tree goto_endif); - bool is_integral_type(tree type); bool is_numeric_type(tree type); tree build_arithmetic_operation(boot::binary_expression *expression, diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index 536b50c..e445a12 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -24,6 +24,7 @@ namespace gcc { void init_ttree(); bool is_pointer_type(tree type); + bool is_integral_type(tree type); bool are_compatible_pointers(tree lhs, tree rhs); class tree_chain_base diff --git a/rakelib/cross.rake b/rakelib/cross.rake index e213bb0..91457c3 100644 --- a/rakelib/cross.rake +++ b/rakelib/cross.rake @@ -2,87 +2,16 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. -} -require 'pathname' require 'uri' require 'net/http' -require 'rake/clean' require 'open3' require 'etc' require_relative 'shared' -GCC_VERSION = "14.2.0" BINUTILS_VERSION = '2.43.1' GLIBC_VERSION = '2.40' KERNEL_VERSION = '5.15.166' -CLOBBER.include TMP - -class BuildTarget - attr_accessor(:build, :gcc, :target, :tmp) - - def gxx - @gcc.gsub 'c', '+' - end - - def sysroot - tmp + 'sysroot' - end - - def rootfs - tmp + 'rootfs' - end - - def tools - tmp + 'tools' - end - - def configuration - case target - when /^riscv[[:digit:]]+-/ - [ - '--with-arch=rv32imafdc', - '--with-abi=ilp32d', - '--with-tune=rocket', - '--with-isa-spec=20191213' - ] - else - [] - end - end -end - -def gcc_verbose(gcc_binary) - read, write = IO.pipe - sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write) - write.close - output = read.read - read.close - output -end - -def find_build_target(gcc_version, task) - gcc_binary = 'gcc' - output = gcc_verbose gcc_binary - - if output.start_with? 'Apple clang' - gcc_binary = "gcc-#{gcc_version.split('.').first}" - output = gcc_verbose gcc_binary - end - result = output - .lines - .each_with_object(BuildTarget.new) do |line, accumulator| - if line.start_with? 'Target: ' - accumulator.build = line.split(' ').last.strip - elsif line.start_with? 'COLLECT_GCC' - accumulator.gcc = line.split('=').last.strip - end - end - result.tmp = TMP - task.with_defaults target: 'riscv32-unknown-linux-gnu' - result.target = task[:target] - result -end - def download_and_unarchive(url, target) case File.extname url.path when '.bz2' @@ -126,7 +55,7 @@ end namespace :cross do desc 'Build cross binutils' task :binutils, [:target] do |_, args| - options = find_build_target GCC_VERSION, args + options = find_build_target GCC_VERSION, args[:target] options.tools.mkpath source_directory = download_and_unarchive( URI.parse("https://ftp.gnu.org/gnu/binutils/binutils-#{BINUTILS_VERSION}.tar.xz"), @@ -157,7 +86,7 @@ namespace :cross do desc 'Build stage 1 GCC' task :gcc1, [:target] do |_, args| - options = find_build_target GCC_VERSION, args + options = find_build_target GCC_VERSION, args[:target] options.tools.mkpath source_directory = download_and_unarchive( URI.parse("https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"), @@ -208,7 +137,7 @@ namespace :cross do desc 'Copy glibc headers' task :headers, [:target] do |_, args| - options = find_build_target GCC_VERSION, args + options = find_build_target GCC_VERSION, args[:target] options.tools.mkpath source_directory = download_and_unarchive( @@ -222,7 +151,7 @@ namespace :cross do desc 'Build linux kernel' task :kernel, [:target] do |_, args| - options = find_build_target GCC_VERSION, args + options = find_build_target GCC_VERSION, args[:target] options.tools.mkpath cwd = download_and_unarchive( @@ -247,7 +176,7 @@ namespace :cross do desc 'Build glibc' task :glibc, [:target] do |_, args| - options = find_build_target GCC_VERSION, args + options = find_build_target GCC_VERSION, args[:target] source_directory = options.tools + "glibc-#{GLIBC_VERSION}" configure_options = [ '--prefix=/usr', @@ -278,7 +207,7 @@ namespace :cross do desc 'Build stage 2 GCC' task :gcc2, [:target] do |_, args| - options = find_build_target GCC_VERSION, args + options = find_build_target GCC_VERSION, args[:target] source_directory = options.tools + "gcc-#{GCC_VERSION}" cwd = options.tools + 'build-gcc' @@ -316,8 +245,8 @@ namespace :cross do sh env, 'make', 'install', chdir: cwd.to_path end - task :init, [:target] do |_, args| - options = find_build_target GCC_VERSION, args + task :init, [:target] do |_, args| + options = find_build_target GCC_VERSION, args[:target] env = { 'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}" } @@ -327,15 +256,3 @@ namespace :cross do 'tools/init.c' end end - -desc 'Build cross toolchain' -task :cross, [:target] => [ - 'cross:binutils', - 'cross:gcc1', - 'cross:headers', - 'cross:kernel', - 'cross:glibc', - 'cross:gcc2', - 'cross:init' -] do -end diff --git a/rakelib/shared.rb b/rakelib/shared.rb index d094511..c0b59b6 100644 --- a/rakelib/shared.rb +++ b/rakelib/shared.rb @@ -2,4 +2,71 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. -} +GCC_VERSION = "14.2.0" + TMP = Pathname.new('./build') + +class BuildTarget + attr_accessor(:build, :gcc, :target, :tmp) + + def gxx + @gcc.gsub 'c', '+' + end + + def sysroot + tmp + 'sysroot' + end + + def rootfs + tmp + 'rootfs' + end + + def tools + tmp + 'tools' + end + + def configuration + case target + when /^riscv[[:digit:]]+-/ + [ + '--with-arch=rv32imafdc', + '--with-abi=ilp32d', + '--with-tune=rocket', + '--with-isa-spec=20191213' + ] + else + [] + end + end +end + +def gcc_verbose(gcc_binary) + read, write = IO.pipe + sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write) + write.close + output = read.read + read.close + output +end + +def find_build_target(gcc_version, target) + gcc_binary = 'gcc' + output = gcc_verbose gcc_binary + + if output.start_with? 'Apple clang' + gcc_binary = "gcc-#{gcc_version.split('.').first}" + output = gcc_verbose gcc_binary + end + result = output + .lines + .each_with_object(BuildTarget.new) do |line, accumulator| + if line.start_with? 'Target: ' + accumulator.build = line.split(' ').last.strip + elsif line.start_with? 'COLLECT_GCC' + accumulator.gcc = line.split('=').last.strip + end + end + result.tmp = TMP + result.target = target + result +end diff --git a/rakelib/tester.rake b/rakelib/tester.rake index 4caae20..4fa5b8c 100644 --- a/rakelib/tester.rake +++ b/rakelib/tester.rake @@ -13,7 +13,7 @@ AS = 'build/rootfs/riscv32-unknown-linux-gnu/bin/as' namespace :test do test_sources = FileList['tests/vm/*.elna', 'tests/vm/*.s'] - compiler = `cabal list-bin elna`.strip + compiler = TMP + 'bin/elna' object_directory = TMP + 'riscv/tests' root_directory = TMP + 'riscv/root' executable_directory = root_directory + 'tests' diff --git a/source.elna b/source.elna index a70149c..dfdc30c 100644 --- a/source.elna +++ b/source.elna @@ -301,7 +301,7 @@ begin if token_end^ <> '\"' then return input end; - token_length := cast(token_end as Word) - cast(input as Word); + token_length := cast(token_end - input as Word); current_token^.value.string_value := cast(calloc(token_length, 1) as pointer to Char); is_valid := true; @@ -464,7 +464,7 @@ begin end; write_c(' '); - i := i + sizeof(Token) + i := i + 1u end; write_c('\n') end; @@ -555,7 +555,7 @@ begin input_pointer := skip_spaces(input_pointer); while input_pointer^ <> '\0' do - tokens := cast(realloc(tokens, tokens_size^ + sizeof(Token)) as pointer to Token); + tokens := cast(reallocarray(tokens, tokens_size^ + 1u, sizeof(Token)) as pointer to Token); current_token := tokens + tokens_size^; if is_alpha(input_pointer^) or input_pointer^ = '_' then @@ -685,7 +685,7 @@ begin end; if current_token^.kind <> 0 then - tokens_size^ := tokens_size^ + sizeof(Token); + tokens_size^ := tokens_size^ + 1u; input_pointer := skip_spaces(input_pointer) else write_s("Lexical analysis error on \""); @@ -712,16 +712,16 @@ begin result^.name := cast(malloc(strlen(tokens^^.value.string_value)) as pointer to Char); strcpy(result^.name, tokens^^.value.string_value); - tokens^ := tokens^ + sizeof(Token) * 2u; - tokens_size := tokens_size - sizeof(Token) * 2u; + tokens^ := tokens^ + 2u; + tokens_size := tokens_size - 2u; write_s(result^.name); write_c('\n'); result^.body := parse_literal(tokens, tokens_size); - tokens^ := tokens^ + sizeof(Token) * 2u; - tokens_size := tokens_size - sizeof(Token) * 2u; + tokens^ := tokens^ + 2u; + tokens_size := tokens_size - 2u; return result end; @@ -737,14 +737,14 @@ begin result^.constants.count := 0u; if tokens^^.kind = TOKEN_CONST then - tokens^ := tokens^ + sizeof(Token); - tokens_size^ := tokens_size^ - sizeof(Token); + tokens^ := tokens^ + 1; + tokens_size^ := tokens_size^ - 1u; while tokens_size^ > 0u and tokens^^.kind = TOKEN_IDENTIFIER do result^.constants.elements := cast( reallocarray(result^.constants.elements, result^.constants.count + 1u, sizeof(pointer to ConstantDefinition)) as pointer to pointer to ConstantDefinition); - current_constant := result^.constants.elements + result^.constants.count * sizeof(pointer to ConstantDefinition); + current_constant := result^.constants.elements + result^.constants.count; result^.constants.count := result^.constants.count + 1u; @@ -768,7 +768,7 @@ begin result^.input := nil; while i < argc do - parameter := argv + i * cast(sizeof(pointer to Char) as Int); + parameter := argv + i; if strcmp(parameter^, "--tokenize") = 0 then result^.tokenize := true diff --git a/tools/init.c b/tools/init.c new file mode 100644 index 0000000..f463bcd --- /dev/null +++ b/tools/init.c @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include +#include +#include + +#define FILENAME_BUFFER_SIZE 256 + +size_t read_command(int descriptor, char *command_buffer) +{ + ssize_t bytes_read = 0; + size_t read_so_far = 0; + + while ((bytes_read = read(descriptor, command_buffer + read_so_far, FILENAME_BUFFER_SIZE - read_so_far - 1)) > 0) + { + read_so_far += bytes_read; + if (read_so_far >= FILENAME_BUFFER_SIZE - 1) + { + break; + } + } + command_buffer[read_so_far] = 0; + return read_so_far; +} + +enum status +{ + status_success, + status_failure, + status_warning, + status_fatal +}; + +unsigned int make_path(char *destination, const char *directory, const char *filename, const char *extension) +{ + unsigned int i = 0; + + for (; i < FILENAME_BUFFER_SIZE; i++) + { + if (directory[i] == 0) + { + break; + } + destination[i] = directory[i]; + } + for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) + { + if (filename[j] == 0) + { + break; + } + destination[i] = filename[j]; + } + if (extension == NULL) + { + goto done; + } + for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) + { + if (extension[j] == 0) + { + break; + } + destination[i] = extension[j]; + } +done: + destination[i] = 0; + + return i; +} + +enum status run_test(const char *file_entry_name) +{ + printf("Running %s. ", file_entry_name); + + char filename[FILENAME_BUFFER_SIZE]; + char command_buffer[FILENAME_BUFFER_SIZE]; + char file_buffer[256]; + int pipe_ends[2]; + + if (pipe(pipe_ends) == -1) + { + perror("pipe"); + return status_fatal; + } + make_path(filename, "./tests/", file_entry_name, NULL); + + int child_pid = fork(); + if (child_pid == -1) + { + return status_fatal; + } + else if (child_pid == 0) + { + close(STDIN_FILENO); + close(STDERR_FILENO); + close(pipe_ends[0]); // Close the read end. + + if (dup2(pipe_ends[1], STDOUT_FILENO) == -1) + { + perror("dup2"); + } + else + { + execl(filename, filename); + perror("execl"); + } + close(STDOUT_FILENO); + close(pipe_ends[1]); + _exit(1); + } + else + { + close(pipe_ends[1]); // Close the write end. + read_command(pipe_ends[0], command_buffer); + close(pipe_ends[0]); + + int wait_status = 0; + + make_path(filename, "./expectations/", file_entry_name, ".txt"); + + FILE *expectation_descriptor = fopen(filename, "r"); + + if (expectation_descriptor == NULL) + { + return status_warning; + } + size_t read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor); + fclose(expectation_descriptor); + + file_buffer[read_from_file] = 0; + for (unsigned int i = 0; ; ++i) + { + if (command_buffer[i] == 0 && file_buffer[i] == 0) + { + fwrite("\n", 1, 1, stdout); + return status_success; + } + else if (command_buffer[i] != file_buffer[i]) + { + printf("Failed. Got:\n%s", command_buffer); + return status_failure; + } + } + } +} + +struct summary +{ + size_t total; + size_t failure; + size_t success; +}; + +void walk() +{ + DIR *directory_stream = opendir("./tests"); + struct dirent *file_entry; + + struct summary test_summary = { .total = 0, .failure = 0, .success = 0 }; + + while ((file_entry = readdir(directory_stream)) != NULL) + { + if (file_entry->d_name[0] == '.') + { + continue; + } + ++test_summary.total; + switch (run_test(file_entry->d_name)) + { + case status_failure: + ++test_summary.failure; + break; + case status_success: + ++test_summary.success; + break; + case status_warning: + break; + case status_fatal: + goto end_walk; + } + } + printf("Successful: %lu, Failed: %lu, Total: %lu.\n", + test_summary.success, test_summary.failure, test_summary.total); +end_walk: + closedir(directory_stream); +} + +int main() +{ + int dev_console = open("/dev/console", O_WRONLY); + if (dev_console != -1) + { + dup2(dev_console, STDOUT_FILENO); + walk(); + close(dev_console); + } + sync(); + reboot(RB_POWER_OFF); + + return 1; +} diff --git a/cli/main.cc b/tools/main.cc similarity index 100% rename from cli/main.cc rename to tools/main.cc