Add multiple of the pointer target size

This commit is contained in:
Eugen Wissner 2025-02-05 13:24:50 +01:00
parent 8b654ed138
commit 5e9b4259ca
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
12 changed files with 380 additions and 133 deletions

View File

@ -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
$<$<COMPILE_LANGUAGE:CXX>:-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})

View File

@ -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

View File

@ -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))

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

204
tools/init.c Normal file
View File

@ -0,0 +1,204 @@
#include <stdio.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/reboot.h>
#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;
}