From e068da3028c1ae30741d2d1ceaca691b71830bfe Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 3 Jul 2026 22:41:48 +0200 Subject: Add a test suite for GCC --- README.md | 5 - config-lang.in | 21 - gcc/Make-lang.in | 5 + gcc/config-lang.in | 2 +- gcc/dg.exp | 29 + gcc/elna-builtins.cc | 274 -------- gcc/elna-diagnostic.cc | 167 ----- gcc/elna-generic.cc | 1277 ---------------------------------- gcc/elna-spec.cc | 31 - gcc/elna-tree.cc | 315 --------- gcc/elna1.cc | 256 ------- gcc/gcc/elna-builtins.cc | 274 ++++++++ gcc/gcc/elna-diagnostic.cc | 167 +++++ gcc/gcc/elna-generic.cc | 1277 ++++++++++++++++++++++++++++++++++ gcc/gcc/elna-spec.cc | 31 + gcc/gcc/elna-tree.cc | 315 +++++++++ gcc/gcc/elna1.cc | 256 +++++++ gcc/gcc/gelna.texi | 135 ++++ gcc/gelna.texi | 135 ---- gcc/testlib/elna-dg.exp | 66 ++ gcc/testlib/elna.exp | 155 +++++ rakelib/gcc.rake | 64 +- testsuite/compilable/empty_proc.elna | 4 + 23 files changed, 2762 insertions(+), 2499 deletions(-) delete mode 100644 config-lang.in create mode 100644 gcc/dg.exp delete mode 100644 gcc/elna-builtins.cc delete mode 100644 gcc/elna-diagnostic.cc delete mode 100644 gcc/elna-generic.cc delete mode 100644 gcc/elna-spec.cc delete mode 100644 gcc/elna-tree.cc delete mode 100644 gcc/elna1.cc create mode 100644 gcc/gcc/elna-builtins.cc create mode 100644 gcc/gcc/elna-diagnostic.cc create mode 100644 gcc/gcc/elna-generic.cc create mode 100644 gcc/gcc/elna-spec.cc create mode 100644 gcc/gcc/elna-tree.cc create mode 100644 gcc/gcc/elna1.cc create mode 100644 gcc/gcc/gelna.texi delete mode 100644 gcc/gelna.texi create mode 100644 gcc/testlib/elna-dg.exp create mode 100644 gcc/testlib/elna.exp create mode 100644 testsuite/compilable/empty_proc.elna diff --git a/README.md b/README.md index 039639a..71b6a6d 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,6 @@ in the `boot/` directory. The frontend requires GCC 15.3.0 (not tested with other versions). -Download the GCC source. Copy the contents of this repository into `gcc/elna` -inside GCC. Finally build GCC enabling the frontend with -`--enable-languages=c,c++,elna`. After the installation the compiler can be -invoked with `$prefix/bin/gelna`. - There is also a `Rakefile` that downloads, builds and installs GCC into the `./build/` subdirectory. The `Rakefile` assumes that ruby and rake, as well as all GCC dependencies are already available in the system. It works under Linux diff --git a/config-lang.in b/config-lang.in deleted file mode 100644 index 1bc1697..0000000 --- a/config-lang.in +++ /dev/null @@ -1,21 +0,0 @@ -# Top level configure fragment for gcc Elna frontend. -# Copyright (C) 2025 Free Software Foundation, Inc. - -# GCC is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. - -# GCC is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with GCC; see the file COPYING3. If not see -# . - -language="elna" -gcc_subdir="elna/gcc" - -. ${srcdir}/elna/gcc/config-lang.in diff --git a/gcc/Make-lang.in b/gcc/Make-lang.in index efc7687..cb57798 100644 --- a/gcc/Make-lang.in +++ b/gcc/Make-lang.in @@ -66,6 +66,11 @@ elna.all.cross: gelna-cross$(exeext) elna.start.encap: gelna$(exeext) elna.rest.encap: +lang_checks += check-elna +lang_checks_parallelized += check-elna +# For description see the check_$lang_parallelize comment in gcc/Makefile.in. +check_elna_parallelize = 10 + # No elna-specific selftests. selftest-elna: diff --git a/gcc/config-lang.in b/gcc/config-lang.in index 0cbbe1f..a003be7 100644 --- a/gcc/config-lang.in +++ b/gcc/config-lang.in @@ -23,7 +23,7 @@ # compilers - value to add to $(COMPILERS) language="elna" -gcc_subdir="elna/gcc" +# gcc_subdir="elna/gcc" compilers="elna1\$(exeext)" diff --git a/gcc/dg.exp b/gcc/dg.exp new file mode 100644 index 0000000..ce424d9 --- /dev/null +++ b/gcc/dg.exp @@ -0,0 +1,29 @@ +# Copyright (C) 2004-2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib elna-dg.exp + +# Initialize dg. +dg-init + +# Main loop. +elna-dg-runtest [lsort [find $srcdir/$subdir *.elna]] "" "" + +# All done. +dg-finish diff --git a/gcc/elna-builtins.cc b/gcc/elna-builtins.cc deleted file mode 100644 index 7c97027..0000000 --- a/gcc/elna-builtins.cc +++ /dev/null @@ -1,274 +0,0 @@ -/* Builtin definitions. - Copyright (C) 2025 Free Software Foundation, Inc. - -GCC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. - -GCC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -. */ - -#include - -#include "elna/gcc/elna-builtins.h" -#include "elna/gcc/elna1.h" -#include "stor-layout.h" -#include "stringpool.h" -#include "elna/gcc/elna-tree.h" - -namespace elna::gcc -{ - void init_ttree() - { - elna_int_type_node = long_integer_type_node; - elna_word_type_node = size_type_node; - elna_char_type_node = unsigned_char_type_node; - elna_pointer_type_node = ptr_type_node; - elna_float_type_node = double_type_node; - - elna_bool_type_node = boolean_type_node; - elna_bool_true_node = boolean_true_node; - elna_bool_false_node = boolean_false_node; - - elna_pointer_nil_node = null_pointer_node; - - elna_string_type_node = make_node(RECORD_TYPE); - tree string_ptr_type = build_pointer_type_for_mode(elna_char_type_node, VOIDmode, true); - - elna_string_length_field_node = build_field(UNKNOWN_LOCATION, - elna_string_type_node, "length", build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST)); - elna_string_ptr_field_node = build_field(UNKNOWN_LOCATION, - elna_string_type_node, "ptr", build_qualified_type(string_ptr_type, TYPE_QUAL_CONST)); - - TYPE_FIELDS(elna_string_type_node) = chainon(elna_string_ptr_field_node, elna_string_length_field_node); - layout_type(elna_string_type_node); - } - - static - tree declare_builtin_type(std::shared_ptr symbol_table, const char *name, tree type) - { - tree identifier = get_identifier(name); - tree type_declaration = build_decl(UNKNOWN_LOCATION, TYPE_DECL, identifier, type); - - symbol_table->enter(name, type_declaration); - - return type_declaration; - } - - std::shared_ptr builtin_symbol_table() - { - auto builtin_table = std::make_shared(); - - declare_builtin_type(builtin_table, "Int", elna_int_type_node); - declare_builtin_type(builtin_table, "Word", elna_word_type_node); - declare_builtin_type(builtin_table, "Char", elna_char_type_node); - declare_builtin_type(builtin_table, "Bool", elna_bool_type_node); - declare_builtin_type(builtin_table, "Pointer", elna_pointer_type_node); - declare_builtin_type(builtin_table, "Float", elna_float_type_node); - - tree string_declaration = declare_builtin_type(builtin_table, "String", elna_string_type_node); - TYPE_NAME(elna_string_type_node) = DECL_NAME(string_declaration); - TYPE_STUB_DECL(elna_string_type_node) = string_declaration; - - return builtin_table; - } - - tree build_composite_type(const std::vector& fields, tree composite_type_node, - std::shared_ptr symbols) - { - for (auto& field : fields) - { - tree rewritten_field = get_inner_alias(field.second, symbols); - tree field_declaration = build_field(UNKNOWN_LOCATION, - composite_type_node, field.first, rewritten_field); - TYPE_FIELDS(composite_type_node) = chainon(TYPE_FIELDS(composite_type_node), field_declaration); - } - layout_type(composite_type_node); - return composite_type_node; - } - - tree build_procedure_type(const boot::procedure_type& procedure, std::shared_ptr symbols) - { - std::vector parameter_types(procedure.parameters.size()); - - for (std::size_t i = 0; i < procedure.parameters.size(); ++i) - { - parameter_types[i] = get_inner_alias(procedure.parameters.at(i), symbols); - } - tree return_type = void_type_node; - - if (!procedure.return_type.proper_type.empty()) - { - return_type = get_inner_alias(procedure.return_type.proper_type, symbols); - } - return build_function_type_array(return_type, procedure.parameters.size(), parameter_types.data()); - } - - tree get_inner_alias(const boot::type& type, std::shared_ptr symbols) - { - if (auto reference = type.get()) - { - auto looked_up = symbols->lookup(reference->identifier); - gcc_assert(looked_up != NULL_TREE); - - return TREE_TYPE(looked_up); - } - else if (auto reference = type.get()) - { - tree composite_type_node = make_node(RECORD_TYPE); - - build_composite_type(reference->fields, composite_type_node, symbols); - - return composite_type_node; - } - else if (auto reference = type.get()) - { - tree composite_type_node = make_node(UNION_TYPE); - - build_composite_type(reference->fields, composite_type_node, symbols); - - return composite_type_node; - } - else if (auto reference = type.get()) - { - return build_enumeration_type(reference->members); - } - else if (auto reference = type.get()) - { - return build_global_pointer_type(get_inner_alias(reference->base, symbols)); - } - else if (auto reference = type.get()) - { - tree base = get_inner_alias(reference->base, symbols); - - return build_static_array_type(base, reference->size); - } - else if (auto reference = type.get()) - { - auto procedure = build_procedure_type(*reference, symbols); - - return build_global_pointer_type(procedure); - } - else if (auto reference = type.get()) - { - return TREE_TYPE(handle_symbol(reference->name, reference, symbols)); - } - return error_mark_node; - } - - tree handle_symbol(const std::string& symbol_name, std::shared_ptr reference, - std::shared_ptr symbols) - { - tree looked_up = symbols->lookup(symbol_name); - - if (looked_up == NULL_TREE) - { - tree type_tree = get_inner_alias(reference->reference, symbols); - looked_up = build_decl(UNKNOWN_LOCATION, TYPE_DECL, - get_identifier(symbol_name.c_str()), type_tree); - - TREE_PUBLIC(looked_up) = 1; - if (is_unique_type(type_tree)) - { - TYPE_NAME(type_tree) = DECL_NAME(looked_up); - TYPE_STUB_DECL(type_tree) = looked_up; - } - else - { - TYPE_NAME(type_tree) = looked_up; - } - symbols->enter(symbol_name, looked_up); - } - return looked_up; - } - - void declare_procedure(const std::string& name, const boot::procedure_info& info, - std::shared_ptr symbols) - { - tree declaration_type = gcc::build_procedure_type(info.symbol, symbols); - tree fndecl = build_fn_decl(name.c_str(), declaration_type); - symbols->enter(name, fndecl); - - if (info.symbol.return_type.no_return) - { - TREE_THIS_VOLATILE(fndecl) = 1; - } - tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, TREE_TYPE(declaration_type)); - DECL_CONTEXT(resdecl) = fndecl; - DECL_RESULT(fndecl) = resdecl; - - tree argument_chain = NULL_TREE; - function_args_iterator parameter_type; - function_args_iter_init(¶meter_type, declaration_type); - - std::vector::const_iterator parameter_name = info.names.cbegin(); - - for (boot::type parameter : info.symbol.parameters) - { - tree declaration_tree = build_decl(UNKNOWN_LOCATION, PARM_DECL, - get_identifier(parameter_name->c_str()), function_args_iter_cond(¶meter_type)); - DECL_CONTEXT(declaration_tree) = fndecl; - DECL_ARG_TYPE(declaration_tree) = function_args_iter_cond(¶meter_type); - - argument_chain = chainon(argument_chain, declaration_tree); - function_args_iter_next(¶meter_type); - ++parameter_name; - } - DECL_ARGUMENTS(fndecl) = argument_chain; - TREE_ADDRESSABLE(fndecl) = 1; - DECL_EXTERNAL(fndecl) = info.is_extern(); - TREE_PUBLIC(fndecl) = info.exported; - } - - tree declare_variable(const std::string& name, const boot::variable_info& info, - std::shared_ptr symbols) - { - auto variable_type = get_inner_alias(info.symbol, symbols); - tree declaration_tree = build_decl(UNKNOWN_LOCATION, VAR_DECL, get_identifier(name.c_str()), variable_type); - - TREE_ADDRESSABLE(declaration_tree) = 1; - DECL_EXTERNAL(declaration_tree) = info.is_extern; - TREE_PUBLIC(declaration_tree) = info.exported; - - symbols->enter(name, declaration_tree); - - return declaration_tree; - } - - void declare_type(const std::string& name, const boot::type_info& info, std::shared_ptr symbols) - { - // The top level symbol table has basic (builtin) types in it which are not aliases. - if (auto alias_type = info.symbol.get()) - { - tree type_declaration = handle_symbol(name, alias_type, symbols); - - TREE_PUBLIC(type_declaration) = info.exported; - } - } - - void rewrite_symbol_table(std::shared_ptr info_table, std::shared_ptr symbols) - { - for (auto& [symbol_name, symbol_info] : *info_table) - { - if (auto type_info = symbol_info->is_type()) - { - declare_type(symbol_name, *type_info, symbols); - } - else if (auto variable_info = symbol_info->is_variable()) - { - declare_variable(symbol_name, *variable_info, symbols); - } - else if (auto procedure_info = symbol_info->is_procedure()) - { - declare_procedure(symbol_name, *procedure_info, symbols); - } - } - } -} diff --git a/gcc/elna-diagnostic.cc b/gcc/elna-diagnostic.cc deleted file mode 100644 index fa32788..0000000 --- a/gcc/elna-diagnostic.cc +++ /dev/null @@ -1,167 +0,0 @@ -/* Elna frontend specific diagnostic routines. - Copyright (C) 2025 Free Software Foundation, Inc. - -GCC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. - -GCC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -. */ - -#include "elna/gcc/elna-diagnostic.h" -#include "elna/gcc/elna-tree.h" -#include "elna/gcc/elna1.h" - -namespace elna::gcc -{ - linemap_guard::linemap_guard(const char *filename) - { - linemap_add(line_table, LC_ENTER, 0, filename, 1); - } - - linemap_guard::~linemap_guard() - { - linemap_add(line_table, LC_LEAVE, 0, NULL, 0); - } - - location_t get_location(const boot::position *position) - { - linemap_line_start(line_table, position->line, 0); - - return linemap_position_for_column(line_table, position->column); - } - - std::string print_aggregate_name(tree type, const std::string& kind_name) - { - if (TYPE_IDENTIFIER(type) == NULL_TREE) - { - return kind_name; - } - else - { - return std::string(IDENTIFIER_POINTER(TYPE_IDENTIFIER(type))); - } - } - - std::string print_type(tree type) - { - gcc_assert(TYPE_P(type)); - - tree unqualified_type = get_qualified_type(type, TYPE_UNQUALIFIED); - tree_code code = TREE_CODE(type); - - if (unqualified_type == elna_int_type_node) - { - return "Int"; - } - else if (unqualified_type == elna_word_type_node) - { - return "Word"; - } - else if (unqualified_type == elna_bool_type_node) - { - return "Bool"; - } - else if (unqualified_type == elna_pointer_type_node) - { - return "Pointer"; - } - else if (unqualified_type == elna_float_type_node) - { - return "Float"; - } - else if (unqualified_type == elna_char_type_node) - { - return "Char"; - } - else if (unqualified_type == elna_string_type_node) - { - return "String"; - } - else if (is_void_type(unqualified_type)) // For procedures without a return type. - { - return "()"; - } - else if (POINTER_TYPE_P(unqualified_type)) - { - tree pointer_target_type = TREE_TYPE(type); - - if (TREE_CODE(pointer_target_type) == FUNCTION_TYPE) - { - return print_type(pointer_target_type); - } - else - { - return std::string("^" + print_type(pointer_target_type)); - } - } - else if (code == FUNCTION_TYPE) - { - std::string output = "proc("; - tree parameter_type = TYPE_ARG_TYPES(type); - while (TREE_VALUE(parameter_type) != void_type_node) - { - output += print_type(TREE_VALUE(parameter_type)); - parameter_type = TREE_CHAIN(parameter_type); - if (TREE_VALUE(parameter_type) == void_type_node) - { - break; - } - else - { - output += ", "; - } - } - output += ')'; - tree return_type = TREE_TYPE(type); - - if (!is_void_type(return_type)) - { - output += " -> " + print_type(return_type); - } - return output; - } - else if (code == ARRAY_TYPE) - { - return "array"; - } - else if (code == RECORD_TYPE) - { - return print_aggregate_name(unqualified_type, "record"); - } - else if (code == UNION_TYPE) - { - return print_aggregate_name(unqualified_type, "union"); - } - else if (code == ENUMERAL_TYPE) - { - return print_aggregate_name(unqualified_type, "enumeration"); - } - else - { - return "<>"; - } - gcc_unreachable(); - } - - void report_errors(const std::deque>& errors) - { - for (const auto& error : errors) - { - location_t gcc_location{ UNKNOWN_LOCATION }; - - if (error->position.line != 0 || error->position.column != 0) - { - gcc_location = get_location(&error->position); - } - error_at(gcc_location, error->what().c_str()); - } - } -} diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc deleted file mode 100644 index 66bd9a2..0000000 --- a/gcc/elna-generic.cc +++ /dev/null @@ -1,1277 +0,0 @@ -/* Visitor generating a GENERIC tree. - Copyright (C) 2025 Free Software Foundation, Inc. - -GCC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. - -GCC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -. */ - -#include - -#include "elna/gcc/elna-generic.h" -#include "elna/gcc/elna-diagnostic.h" -#include "elna/gcc/elna1.h" -#include "elna/gcc/elna-builtins.h" - -#include "ggc.h" -#include "function.h" -#include "cgraph.h" -#include "gimplify.h" -#include "stringpool.h" -#include "diagnostic.h" -#include "realmpfr.h" -#include "varasm.h" -#include "fold-const.h" -#include "langhooks.h" - -namespace elna::gcc -{ - generic_visitor::generic_visitor(std::shared_ptr symbol_table, boot::symbol_bag bag) - : bag(bag), symbols(symbol_table) - { - } - - void generic_visitor::build_procedure_call(location_t call_location, - tree procedure_address, const std::vector& arguments) - { - vec *argument_trees = nullptr; - tree symbol_type = TREE_TYPE(TREE_TYPE(procedure_address)); - - tree current_parameter = TYPE_ARG_TYPES(symbol_type); - - vec_alloc(argument_trees, arguments.size()); - for (boot::expression *const argument : arguments) - { - location_t argument_location = get_location(&argument->position()); - if (VOID_TYPE_P(TREE_VALUE(current_parameter))) - { - error_at(argument_location, "Too many arguments, expected %i, got %lu", - list_length(TYPE_ARG_TYPES(symbol_type)) - 1, arguments.size()); - this->current_expression = error_mark_node; - break; - } - argument->accept(this); - this->current_expression = prepare_rvalue(this->current_expression); - if (!is_assignable_from(TREE_VALUE(current_parameter), this->current_expression)) - { - error_at(argument_location, - "Cannot assign value of type '%s' to variable of type '%s'", - print_type(TREE_TYPE(this->current_expression)).c_str(), - print_type(TREE_VALUE(current_parameter)).c_str()); - this->current_expression = error_mark_node; - } - current_parameter = TREE_CHAIN(current_parameter); - argument_trees->quick_push(this->current_expression); - } - tree stmt = fold_build_call_array_loc(call_location, TREE_TYPE(symbol_type), - procedure_address, vec_safe_length(argument_trees), vec_safe_address(argument_trees)); - - if (!VOID_TYPE_P(TREE_VALUE(current_parameter))) - { - error_at(call_location, "Too few arguments, expected %i, got %lu", - list_length(TYPE_ARG_TYPES(symbol_type)) - 1, arguments.size()); - this->current_expression = error_mark_node; - } - else - { - this->current_expression = stmt; - } - } - - void generic_visitor::build_record_call(location_t call_location, - tree symbol, const std::vector& arguments) - { - vec *tree_arguments = nullptr; - tree record_fields = TYPE_FIELDS(symbol); - for (boot::expression *const argument : arguments) - { - location_t argument_location = get_location(&argument->position()); - - if (is_void_type(record_fields)) - { - error_at(argument_location, "Too many arguments, expected %i, got %lu", - list_length(TYPE_FIELDS(symbol)), arguments.size()); - this->current_expression = error_mark_node; - break; - } - argument->accept(this); - tree unqualified_field = get_qualified_type(TREE_TYPE(record_fields), TYPE_UNQUALIFIED); - if (!is_assignable_from(unqualified_field, this->current_expression)) - { - error_at(argument_location, - "Cannot assign value of type '%s' to variable of type '%s'", - print_type(TREE_TYPE(this->current_expression)).c_str(), - print_type(TREE_TYPE(record_fields)).c_str()); - this->current_expression = error_mark_node; - } - CONSTRUCTOR_APPEND_ELT(tree_arguments, record_fields, this->current_expression); - record_fields = TREE_CHAIN(record_fields); - } - if (!is_void_type(record_fields)) - { - error_at(call_location, "Too few arguments, expected %i, got %lu", - list_length(TYPE_FIELDS(symbol)), arguments.size()); - this->current_expression = error_mark_node; - } - else - { - this->current_expression = build_constructor(symbol, tree_arguments); - } - } - - void generic_visitor::build_assert_builtin(location_t call_location, - const std::vector& arguments) - { - if (arguments.size() != 1) - { - error_at(call_location, "assert expects exactly one boolean argument, got %lu", arguments.size()); - this->current_expression = error_mark_node; - } - else - { - arguments.at(0)->accept(this); - tree argument_type = TREE_TYPE(this->current_expression); - - if (argument_type != elna_bool_type_node) - { - error_at(call_location, "assert expects exactly one boolean argument, got %s", - print_type(argument_type).c_str()); - this->current_expression = error_mark_node; - } - tree constant_expression = extract_constant(this->current_expression); - if (constant_expression == boolean_false_node) - { - this->current_expression = call_built_in(call_location, "__builtin_unreachable", void_type_node); - } - else if (constant_expression != boolean_true_node) - { - tree assert_expression = call_built_in(call_location, "__builtin_trap", void_type_node); - this->current_expression = build3(COND_EXPR, void_type_node, this->current_expression, - NULL_TREE, assert_expression); - } - else - { - this->current_expression = NULL_TREE; - } - } - } - - bool generic_visitor::build_builtin_procedures(boot::procedure_call *call) - { - location_t call_location = get_location(&call->position()); - - if (boot::named_expression *named_call = call->callable().is_named()) - { - if (named_call->name == "assert") - { - build_assert_builtin(call_location, call->arguments); - return true; - } - } - return false; - } - - void generic_visitor::visit(boot::procedure_call *call) - { - if (build_builtin_procedures(call)) - { - return; - } - location_t call_location = get_location(&call->position()); - call->callable().accept(this); - - tree expression_type = TYPE_P(this->current_expression) - ? this->current_expression - : TREE_TYPE(this->current_expression); - - if (TREE_CODE(expression_type) == RECORD_TYPE) - { - build_record_call(call_location, expression_type, call->arguments); - } - else if (TREE_CODE(expression_type) == FUNCTION_TYPE) - { - this->current_expression = build1(ADDR_EXPR, - build_global_pointer_type(expression_type), this->current_expression); - build_procedure_call(call_location, this->current_expression, call->arguments); - } - else if (POINTER_TYPE_P(expression_type) && TREE_CODE(TREE_TYPE(expression_type)) == FUNCTION_TYPE) - { - build_procedure_call(call_location, this->current_expression, call->arguments); - } - else - { - error_at(call_location, "'%s' cannot be called, it is neither a procedure nor record", - print_type(expression_type).c_str()); - this->current_expression = error_mark_node; - } - } - - void generic_visitor::visit(boot::cast_expression *expression) - { - tree cast_target = get_inner_alias(expression->expression_type, this->symbols->scope()); - - expression->value().accept(this); - tree cast_source = TREE_TYPE(this->current_expression); - - if (is_castable_type(cast_target) && (is_castable_type(cast_source))) - { - this->current_expression = build1_loc(get_location(&expression->position()), CONVERT_EXPR, - cast_target, this->current_expression); - } - else - { - error_at(get_location(&expression->position()), "Type '%s' cannot be converted to '%s'", - print_type(cast_source).c_str(), print_type(cast_target).c_str()); - this->current_expression = error_mark_node; - } - } - - void generic_visitor::visit(boot::program *program) - { - visit(static_cast(program)); - - tree declaration_type = build_function_type_list(elna_int_type_node, - elna_int_type_node, - build_global_pointer_type(build_global_pointer_type(elna_char_type_node)), - NULL_TREE); - tree fndecl = build_fn_decl("main", declaration_type); - - tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, integer_type_node); - DECL_CONTEXT(resdecl) = fndecl; - DECL_RESULT(fndecl) = resdecl; - - push_struct_function(fndecl, false); - DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc(); - - enter_scope(); - - tree parameter_type = TYPE_ARG_TYPES(declaration_type); - for (const char *argument_name : std::array{ "count", "parameters" }) - { - tree declaration_tree = build_decl(UNKNOWN_LOCATION, PARM_DECL, - get_identifier(argument_name), TREE_VALUE(parameter_type)); - DECL_CONTEXT(declaration_tree) = fndecl; - DECL_ARG_TYPE(declaration_tree) = TREE_VALUE(parameter_type); - - this->symbols->enter(argument_name, declaration_tree); - DECL_ARGUMENTS(fndecl) = chainon(DECL_ARGUMENTS(fndecl), declaration_tree); - parameter_type = TREE_CHAIN(parameter_type); - } - visit_statements(program->body); - tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(fndecl), - build_int_cst_type(integer_type_node, 0)); - tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); - append_statement(return_stmt); - tree mapping = leave_scope(); - - BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; - DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); - DECL_SAVED_TREE(fndecl) = mapping; - - DECL_EXTERNAL(fndecl) = 0; - DECL_PRESERVE_P(fndecl) = 1; - - pop_cfun(); - gimplify_function_tree(fndecl); - cgraph_node::finalize_function(fndecl, true); - } - - void generic_visitor::visit(boot::unit *unit) - { - for (boot::import_declaration *const declaration : unit->imports) - { - declaration->accept(this); - } - for (boot::constant_declaration *const constant : unit->constants) - { - constant->accept(this); - } - for (boot::variable_declaration *const variable : unit->variables) - { - variable->accept(this); - } - for (boot::procedure_declaration *const procedure : unit->procedures) - { - procedure->accept(this); - } - } - - void generic_visitor::visit(boot::procedure_declaration *definition) - { - tree fndecl = this->symbols->lookup(definition->identifier.name); - - if (!definition->body.has_value()) - { - return; - } - push_struct_function(fndecl, false); - DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc(); - - enter_scope(); - this->bag.enter(this->bag.lookup(definition->identifier.name)->is_procedure()->scope); - - tree argument_chain = DECL_ARGUMENTS(fndecl); - for (; argument_chain != NULL_TREE; argument_chain = TREE_CHAIN(argument_chain)) - { - this->symbols->enter(IDENTIFIER_POINTER(DECL_NAME(argument_chain)), argument_chain); - } - for (boot::constant_declaration *const constant : definition->body.value().constants()) - { - constant->accept(this); - } - for (boot::variable_declaration *const variable : definition->body.value().variables()) - { - variable->accept(this); - } - visit_statements(definition->body.value().body()); - - tree mapping = leave_scope(); - this->bag.leave(); - - BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; - DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); - DECL_SAVED_TREE(fndecl) = mapping; - - DECL_PRESERVE_P(fndecl) = 1; - - pop_cfun(); - gimplify_function_tree(fndecl); - cgraph_node::finalize_function(fndecl, true); - } - - void generic_visitor::enter_scope() - { - this->symbols = std::make_shared(this->symbols); - - // Chain the binding levels. - struct binding_level *new_level = ggc_cleared_alloc(); - new_level->level_chain = f_binding_level; - new_level->statement_list = alloc_stmt_list(); - f_binding_level = new_level; - } - - tree generic_visitor::leave_scope() - { - // Variables are only defined in the top function scope. - tree variables = f_binding_level->level_chain == nullptr ? f_names : NULL_TREE; - tree new_block = build_block(variables, f_binding_level->blocks, NULL_TREE, NULL_TREE); - - for (tree it = f_binding_level->blocks; it != NULL_TREE; it = BLOCK_CHAIN(it)) - { - BLOCK_SUPERCONTEXT(it) = new_block; - } - tree bind_expr = build3(BIND_EXPR, void_type_node, variables, chain_defer(), new_block); - this->symbols = this->symbols->scope(); - - f_binding_level = f_binding_level->level_chain; - - if (f_binding_level != nullptr) - { - f_binding_level->blocks = chainon(f_binding_level->blocks, new_block); - } - return bind_expr; - } - - void generic_visitor::visit(boot::literal *literal) - { - this->current_expression = build_int_cst(elna_int_type_node, literal->value); - } - - void generic_visitor::visit(boot::literal *literal) - { - this->current_expression = build_int_cstu(elna_word_type_node, literal->value); - } - - void generic_visitor::visit(boot::literal *literal) - { - REAL_VALUE_TYPE real_value1; - - mpfr_t number; - mpfr_init2(number, SIGNIFICAND_BITS); - mpfr_set_d(number, literal->value, MPFR_RNDN); - - real_from_mpfr(&real_value1, number, double_type_node, MPFR_RNDN); - - this->current_expression = build_real(double_type_node, real_value1); - - mpfr_clear(number); - } - - void generic_visitor::visit(boot::literal *boolean) - { - this->current_expression = boolean->value ? boolean_true_node : boolean_false_node; - } - - void generic_visitor::visit(boot::literal *character) - { - this->current_expression = build_int_cstu(elna_char_type_node, character->value); - } - - void generic_visitor::visit(boot::literal *) - { - this->current_expression = elna_pointer_nil_node; - } - - void generic_visitor::visit(boot::literal *string) - { - tree index_constant = build_int_cstu(elna_word_type_node, string->value.size()); - tree string_type = build_array_type(elna_char_type_node, build_index_type(index_constant)); - - tree string_literal = build_string(string->value.size(), string->value.c_str()); - TREE_TYPE(string_literal) = string_type; - TREE_CONSTANT(string_literal) = 1; - TREE_READONLY(string_literal) = 1; - TREE_STATIC(string_literal) = 1; - - string_type = TREE_TYPE(elna_string_ptr_field_node); - string_literal = build4(ARRAY_REF, elna_char_type_node, - string_literal, integer_zero_node, NULL_TREE, NULL_TREE); - string_literal = build1(ADDR_EXPR, string_type, string_literal); - - vec *elms = nullptr; - CONSTRUCTOR_APPEND_ELT(elms, elna_string_ptr_field_node, string_literal); - CONSTRUCTOR_APPEND_ELT(elms, elna_string_length_field_node, index_constant); - - this->current_expression = build_constructor(elna_string_type_node, elms); - } - - tree generic_visitor::build_arithmetic_operation(boot::binary_expression *expression, - tree_code operator_code, tree left, tree right) - { - return build_binary_operation(is_numeric_type(TREE_TYPE(left)), - expression, operator_code, left, right, TREE_TYPE(left)); - } - - tree generic_visitor::build_comparison_operation(boot::binary_expression *expression, - tree_code operator_code, tree left, tree right) - { - return build_binary_operation(is_numeric_type(TREE_TYPE(left)) || POINTER_TYPE_P(TREE_TYPE(left)), - expression, operator_code, left, right, elna_bool_type_node); - } - - tree generic_visitor::build_bit_logic_operation(boot::binary_expression *expression, tree left, tree right) - { - location_t expression_location = get_location(&expression->position()); - tree left_type = TREE_TYPE(left); - tree right_type = TREE_TYPE(right); - tree_code logical_code, bit_code; - - if (expression->operation() == boot::binary_operator::conjunction) - { - bit_code = BIT_AND_EXPR; - logical_code = TRUTH_ANDIF_EXPR; - } - else if (expression->operation() == boot::binary_operator::disjunction) - { - bit_code = BIT_IOR_EXPR; - logical_code = TRUTH_ORIF_EXPR; - } - else if (expression->operation() == boot::binary_operator::exclusive_disjunction) - { - bit_code = BIT_XOR_EXPR; - logical_code = TRUTH_XOR_EXPR; - } - else - { - gcc_unreachable(); - } - if (left_type == elna_bool_type_node) - { - return build2_loc(expression_location, logical_code, elna_bool_type_node, left, right); - } - else if (is_integral_type(left_type)) - { - return build2_loc(expression_location, bit_code, left_type, left, right); - } - else - { - error_at(expression_location, "Invalid operands of type '%s' and '%s' for operator %s", - print_type(left_type).c_str(), print_type(right_type).c_str(), - boot::print_binary_operator(expression->operation())); - return error_mark_node; - } - } - - tree generic_visitor::build_equality_operation(boot::binary_expression *expression, tree left, tree right) - { - location_t expression_location = get_location(&expression->position()); - tree_code equality_code, combination_code; - - if (expression->operation() == boot::binary_operator::equals) - { - equality_code = EQ_EXPR; - combination_code = TRUTH_ANDIF_EXPR; - } - else if (expression->operation() == boot::binary_operator::not_equals) - { - equality_code = NE_EXPR; - combination_code = TRUTH_ORIF_EXPR; - } - else - { - gcc_unreachable(); - } - if (TREE_TYPE(left) == elna_string_type_node) - { - tree lhs_length = build3(COMPONENT_REF, TREE_TYPE(elna_string_length_field_node), - left, elna_string_length_field_node, NULL_TREE); - tree lhs_ptr = build3(COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), - left, elna_string_ptr_field_node, NULL_TREE); - - tree rhs_length = build3(COMPONENT_REF, TREE_TYPE(elna_string_length_field_node), - right, elna_string_length_field_node, NULL_TREE); - tree rhs_ptr = build3(COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), - right, elna_string_ptr_field_node, NULL_TREE); - - tree length_equality = build2(equality_code, elna_bool_type_node, lhs_length, rhs_length); - tree memcmp_call = call_built_in(UNKNOWN_LOCATION, "__builtin_memcmp", integer_type_node, - lhs_ptr, rhs_ptr, lhs_length); - tree equals_zero = build2(equality_code, elna_bool_type_node, memcmp_call, integer_zero_node); - - return build2(combination_code, elna_bool_type_node, length_equality, equals_zero); - } - else - { - return build2_loc(expression_location, equality_code, elna_bool_type_node, left, right); - } - } - - void generic_visitor::visit(boot::binary_expression *expression) - { - expression->lhs().accept(this); - tree left = this->current_expression; - tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED); - - expression->rhs().accept(this); - tree right = this->current_expression; - tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED); - - location_t expression_location = get_location(&expression->position()); - - if ((POINTER_TYPE_P(left_type) || POINTER_TYPE_P(right_type)) - && (expression->operation() == boot::binary_operator::sum - || expression->operation() == boot::binary_operator::subtraction)) - { - this->current_expression = do_pointer_arithmetic(expression->operation(), - left, right, expression_location); - if (this->current_expression == error_mark_node) - { - error_at(expression_location, - "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(elna_int_type_node, this->current_expression); - } - return; - } - if (left_type != right_type - && !are_compatible_pointers(left_type, right) - && !are_compatible_pointers(right_type, left) - && !(is_integral_type(left_type) && right_type == elna_word_type_node)) - { - error_at(expression_location, - "invalid operands of type '%s' and '%s' for operator %s", - print_type(left_type).c_str(), print_type(right_type).c_str(), - boot::print_binary_operator(expression->operation())); - this->current_expression = error_mark_node; - return; - } - switch (expression->operation()) - { - case boot::binary_operator::sum: - this->current_expression = build_arithmetic_operation(expression, PLUS_EXPR, left, right); - break; - case boot::binary_operator::subtraction: - this->current_expression = build_arithmetic_operation(expression, MINUS_EXPR, left, right); - break; - case boot::binary_operator::division: - this->current_expression = build_arithmetic_operation(expression, TRUNC_DIV_EXPR, left, right); - break; - case boot::binary_operator::remainder: - this->current_expression = build_arithmetic_operation(expression, TRUNC_MOD_EXPR, left, right); - break; - case boot::binary_operator::multiplication: - this->current_expression = build_arithmetic_operation(expression, MULT_EXPR, left, right); - break; - case boot::binary_operator::less: - this->current_expression = build_comparison_operation(expression, LT_EXPR, left, right); - break; - case boot::binary_operator::greater: - this->current_expression = build_comparison_operation(expression, GT_EXPR, left, right); - break; - case boot::binary_operator::less_equal: - this->current_expression = build_comparison_operation(expression, LE_EXPR, left, right); - break; - case boot::binary_operator::greater_equal: - this->current_expression = build_comparison_operation(expression, GE_EXPR, left, right); - break; - case boot::binary_operator::conjunction: - this->current_expression = build_bit_logic_operation(expression, left, right); - break; - case boot::binary_operator::disjunction: - this->current_expression = build_bit_logic_operation(expression, left, right); - break; - case boot::binary_operator::exclusive_disjunction: - this->current_expression = build_bit_logic_operation(expression, left, right); - break; - case boot::binary_operator::equals: - this->current_expression = build_equality_operation(expression, left, right); - break; - case boot::binary_operator::not_equals: - this->current_expression = build_equality_operation(expression, left, right); - break; - case boot::binary_operator::shift_left: - this->current_expression = build_binary_operation( - is_numeric_type(left_type) && right_type == elna_word_type_node, - expression, LSHIFT_EXPR, left, right, left_type); - break; - case boot::binary_operator::shift_right: - this->current_expression = build_binary_operation( - is_numeric_type(left_type) && right_type == elna_word_type_node, - expression, RSHIFT_EXPR, left, right, left_type); - break; - } - } - - void generic_visitor::visit(boot::unary_expression *expression) - { - expression->operand().accept(this); - location_t location = get_location(&expression->position()); - - switch (expression->operation()) - { - case boot::unary_operator::reference: - this->current_expression = prepare_rvalue(this->current_expression); - TREE_ADDRESSABLE(this->current_expression) = 1; - this->current_expression = build_fold_addr_expr_with_type_loc(location, - this->current_expression, - build_global_pointer_type(TREE_TYPE(this->current_expression))); - TREE_NO_TRAMPOLINE(this->current_expression) = 1; - break; - case boot::unary_operator::negation: - if (TREE_TYPE(this->current_expression) == elna_bool_type_node) - { - this->current_expression = build1_loc(location, TRUTH_NOT_EXPR, - boolean_type_node, this->current_expression); - } - else if (is_integral_type(TREE_TYPE(this->current_expression))) - { - this->current_expression = build1_loc(location, BIT_NOT_EXPR, - TREE_TYPE(this->current_expression), this->current_expression); - } - else - { - error_at(location, "type '%s' cannot be negated", - print_type(TREE_TYPE(this->current_expression)).c_str()); - this->current_expression = error_mark_node; - } - break; - case boot::unary_operator::minus: - if (is_integral_type(TREE_TYPE(this->current_expression))) - { - this->current_expression = fold_build1(NEGATE_EXPR, TREE_TYPE(this->current_expression), - this->current_expression); - } - else - { - error_at(location, "type '%s' cannot be negated", - print_type(TREE_TYPE(this->current_expression)).c_str()); - this->current_expression = error_mark_node; - } - } - } - - void generic_visitor::visit(boot::constant_declaration *definition) - { - location_t definition_location = get_location(&definition->position()); - definition->body().accept(this); - - if (assert_constant(definition_location)) - { - this->current_expression = fold_init(this->current_expression); - } - else - { - this->current_expression = NULL_TREE; - return; - } - tree definition_tree = build_decl(definition_location, CONST_DECL, - get_identifier(definition->identifier.name.c_str()), TREE_TYPE(this->current_expression)); - auto result = this->symbols->enter(definition->identifier.name, definition_tree); - - if (result) - { - DECL_INITIAL(definition_tree) = this->current_expression; - TREE_CONSTANT(definition_tree) = 1; - TREE_READONLY(definition_tree) = 1; - TREE_PUBLIC(definition_tree) = definition->identifier.exported; - - if (!lang_hooks.decls.global_bindings_p()) - { - auto declaration_statement = build1_loc(definition_location, DECL_EXPR, - void_type_node, definition_tree); - append_statement(declaration_statement); - } - } - else - { - error_at(definition_location, "Variable '%s' already declared in this scope", - definition->identifier.name.c_str()); - } - this->current_expression = NULL_TREE; - } - - void generic_visitor::visit(boot::variable_declaration *declaration) - { - for (const auto& variable_identifier : declaration->identifiers) - { - location_t declaration_location = get_location(&declaration->position()); - tree declaration_tree = this->symbols->lookup(variable_identifier.name); - - if (declaration_tree == NULL_TREE) - { - auto variable_symbol = this->bag.lookup(variable_identifier.name)->is_variable(); - - declaration_tree = declare_variable(variable_identifier.name, *variable_symbol, this->symbols); - } - // Set initializer if given. - if (declaration->body != nullptr) - { - declaration->body->accept(this); - if (is_assignable_from(TREE_TYPE(declaration_tree), this->current_expression)) - { - DECL_INITIAL(declaration_tree) = this->current_expression; - } - else - { - error_at(declaration_location, "Cannot initialize variable of type '%s' with a value of type '%s'", - print_type(TREE_TYPE(declaration_tree)).c_str(), - print_type(TREE_TYPE(this->current_expression)).c_str()); - } - } - else if (!declaration->is_extern && POINTER_TYPE_P(TREE_TYPE(declaration_tree))) - { - DECL_INITIAL(declaration_tree) = elna_pointer_nil_node; - } - this->current_expression = NULL_TREE; - - if (lang_hooks.decls.global_bindings_p()) - { - TREE_STATIC(declaration_tree) = !variable_identifier.exported && !declaration->is_extern; - varpool_node::get_create(declaration_tree); - varpool_node::finalize_decl(declaration_tree); - } - else - { - DECL_CONTEXT(declaration_tree) = current_function_decl; - f_names = chainon(f_names, declaration_tree); - - auto declaration_statement = build1_loc(declaration_location, DECL_EXPR, - void_type_node, declaration_tree); - append_statement(declaration_statement); - } - } - } - - void generic_visitor::visit(boot::named_expression *expression) - { - auto symbol = this->symbols->lookup(expression->name); - - if (symbol == NULL_TREE) - { - error_at(get_location(&expression->position()), "Symbol '%s' not declared in the current scope", - expression->name.c_str()); - this->current_expression = error_mark_node; - } - else - { - this->current_expression = symbol; - } - } - - void generic_visitor::visit(boot::array_access_expression *expression) - { - expression->base().accept(this); - tree designator = this->current_expression; - location_t location = get_location(&expression->position()); - - expression->index().accept(this); - if (!is_integral_type(TREE_TYPE(this->current_expression))) - { - error_at(location, "Type '%s' cannot be used as index", - print_type(TREE_TYPE(this->current_expression)).c_str()); - this->current_expression = error_mark_node; - return; - } - tree offset = fold_convert(elna_word_type_node, this->current_expression); - - if (TREE_CODE(TREE_TYPE(designator)) == ARRAY_TYPE) - { - tree element_type = TREE_TYPE(TREE_TYPE(designator)); - - this->current_expression = build4_loc(location, - ARRAY_REF, element_type, designator, offset, size_one_node, NULL_TREE); - } - else if (TREE_TYPE(designator) == elna_string_type_node) - { - offset = build2(MINUS_EXPR, elna_word_type_node, offset, size_one_node); - tree string_ptr = build3_loc(location, COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), - designator, elna_string_ptr_field_node, NULL_TREE); - - tree target_pointer = do_pointer_arithmetic(boot::binary_operator::sum, string_ptr, offset, location); - - this->current_expression = build1_loc(location, INDIRECT_REF, - elna_char_type_node, target_pointer); - } - else - { - error_at(location, "Indexing is not allowed on type '%s'", - print_type(TREE_TYPE(designator)).c_str()); - this->current_expression = error_mark_node; - } - } - - bool generic_visitor::expect_trait_type_only(boot::traits_expression *trait) - { - if (trait->parameters.size() != 1) - { - error_at(get_location(&trait->position()), "Trait '%s' expects 1 argument, got %lu", - trait->name.c_str(), trait->parameters.size()); - this->current_expression = error_mark_node; - return false; - } - this->current_expression = get_inner_alias(trait->types.front(), this->symbols); - - return this->current_expression != error_mark_node; - } - - bool generic_visitor::expect_trait_for_integral_type(boot::traits_expression *trait) - { - if (!expect_trait_type_only(trait)) - { - return false; - } - else if (!is_integral_type(this->current_expression) && TREE_CODE(this->current_expression) != ENUMERAL_TYPE) - { - error_at(get_location(&trait->position()), "Type '%s' does not support trait '%s'", - print_type(this->current_expression).c_str(), trait->name.c_str()); - this->current_expression = error_mark_node; - return false; - } - return true; - } - - void generic_visitor::visit(boot::traits_expression *trait) - { - location_t trait_location = get_location(&trait->position()); - - if (trait->name == "size") - { - if (expect_trait_type_only(trait)) - { - this->current_expression = build1_loc(trait_location, CONVERT_EXPR, elna_word_type_node, - size_in_bytes(this->current_expression)); - } - } - else if (trait->name == "alignment") - { - if (expect_trait_type_only(trait)) - { - this->current_expression = build_int_cstu(elna_word_type_node, - TYPE_ALIGN_UNIT(this->current_expression)); - } - } - else if (trait->name == "min") - { - if (expect_trait_for_integral_type(trait)) - { - this->current_expression = TYPE_MIN_VALUE(this->current_expression); - } - } - else if (trait->name == "max") - { - if (expect_trait_for_integral_type(trait)) - { - this->current_expression = TYPE_MAX_VALUE(this->current_expression); - } - } - else if (trait->name == "offset") - { - if (trait->parameters.size() != 2) - { - error_at(trait_location, "Trait '%s' expects 2 arguments, got %lu", - trait->name.c_str(), trait->parameters.size()); - this->current_expression = error_mark_node; - return; - } - this->current_expression = get_inner_alias(trait->types.front(), this->symbols); - auto field_type = trait->parameters.at(1)->is_named(); - - if (field_type == nullptr) - { - error_at(trait_location, - "The second argument to the offset trait is expected to be a field name," - "got a type expression"); - this->current_expression = error_mark_node; - return; - } - tree field_declaration = find_field_by_name(trait_location, this->current_expression, field_type->name); - - if (field_declaration != error_mark_node) - { - this->current_expression = build1(CONVERT_EXPR, elna_word_type_node, - byte_position(field_declaration)); - } - else - { - this->current_expression = error_mark_node; - } - } - else - { - error_at(get_location(&trait->position()), "Trait '%s' is unknown", trait->name.c_str()); - this->current_expression = error_mark_node; - } - } - - void generic_visitor::visit(boot::field_access_expression *expression) - { - expression->base().accept(this); - location_t expression_location = get_location(&expression->position()); - tree aggregate_type = TREE_TYPE(this->current_expression); - - if (TREE_CODE(aggregate_type) == ARRAY_TYPE && expression->field() == "length") - { - this->current_expression = convert(build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST), - TYPE_MAX_VALUE(TYPE_DOMAIN(aggregate_type))); - } - else if (TREE_CODE(aggregate_type) == ARRAY_TYPE && expression->field() == "ptr") - { - tree ptr_type = build_global_pointer_type(TREE_TYPE(aggregate_type)); - this->current_expression = build1(ADDR_EXPR, - build_qualified_type(ptr_type, TYPE_QUAL_CONST), this->current_expression); - } - else if (TREE_CODE(aggregate_type) == ENUMERAL_TYPE) - { - tree iterator{ NULL_TREE }; - - for (iterator = TYPE_VALUES(aggregate_type); iterator != NULL_TREE; iterator = TREE_CHAIN(iterator)) - { - if (IDENTIFIER_POINTER(TREE_PURPOSE(iterator)) == expression->field()) - { - this->current_expression = TREE_VALUE(iterator); - return; - } - } - this->current_expression = error_mark_node; - error_at(expression_location, "Unknown enumeration member '%s'", expression->field().c_str()); - } - else - { - tree field_declaration = find_field_by_name(expression_location, - TREE_TYPE(this->current_expression), expression->field()); - - if (field_declaration != error_mark_node) - { - this->current_expression = build3_loc(expression_location, COMPONENT_REF, - TREE_TYPE(field_declaration), this->current_expression, - field_declaration, NULL_TREE); - } - } - } - - void generic_visitor::visit(boot::dereference_expression *expression) - { - expression->base().accept(this); - location_t expression_location = get_location(&expression->position()); - tree expression_type = TREE_TYPE(this->current_expression); - - if (POINTER_TYPE_P(expression_type)) - { - this->current_expression = build1_loc(expression_location, INDIRECT_REF, - TREE_TYPE(expression_type), this->current_expression); - } - else - { - error_at(expression_location, "Type '%s' cannot be dereferenced, it is not a pointer", - print_type(expression_type).c_str()); - this->current_expression = error_mark_node; - } - } - - void generic_visitor::visit(boot::assign_statement *statement) - { - statement->lvalue().accept(this); - - tree lvalue = this->current_expression; - location_t statement_location = get_location(&statement->position()); - - statement->rvalue().accept(this); - tree rvalue = prepare_rvalue(this->current_expression); - - if (TREE_CODE(lvalue) == CONST_DECL) - { - error_at(statement_location, "Cannot modify constant '%s'", - statement->lvalue().is_named()->name.c_str()); - } - else if (TYPE_READONLY(TREE_TYPE(lvalue))) - { - error_at(statement_location, "Cannot modify a constant expression of type '%s'", - print_type(TREE_TYPE(lvalue)).c_str()); - } - else if (is_assignable_from(TREE_TYPE(lvalue), rvalue)) - { - tree assignment = build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue, rvalue); - - append_statement(assignment); - } - else - { - error_at(statement_location, "Cannot assign value of type '%s' to variable of type '%s'", - print_type(TREE_TYPE(rvalue)).c_str(), - print_type(TREE_TYPE(lvalue)).c_str()); - } - this->current_expression = NULL_TREE; - } - - void generic_visitor::visit(boot::if_statement *statement) - { - tree endif_label_decl = create_artificial_label(UNKNOWN_LOCATION); - tree goto_endif = build1(GOTO_EXPR, void_type_node, endif_label_decl); - - make_if_branch(statement->body(), goto_endif); - - for (const auto branch : statement->branches) - { - make_if_branch(*branch, goto_endif); - } - if (statement->alternative != nullptr) - { - enter_scope(); - visit_statements(*statement->alternative); - tree mapping = leave_scope(); - append_statement(mapping); - } - tree endif_label_expr = build1(LABEL_EXPR, void_type_node, endif_label_decl); - append_statement(endif_label_expr); - this->current_expression = NULL_TREE; - } - - void generic_visitor::make_if_branch(boot::conditional_statements& branch, tree goto_endif) - { - branch.prerequisite().accept(this); - - if (TREE_TYPE(this->current_expression) != elna_bool_type_node) - { - error_at(get_location(&branch.prerequisite().position()), - "Expected expression of boolean type but its type is %s", - print_type(TREE_TYPE(this->current_expression)).c_str()); - this->current_expression = error_mark_node; - return; - } - tree then_label_decl = build_label_decl("then", UNKNOWN_LOCATION); - tree goto_then = build1(GOTO_EXPR, void_type_node, then_label_decl); - - tree else_label_decl = build_label_decl("else", UNKNOWN_LOCATION); - tree goto_else = build1(GOTO_EXPR, void_type_node, else_label_decl); - - auto cond_expr = build3(COND_EXPR, void_type_node, this->current_expression, goto_then, goto_else); - append_statement(cond_expr); - - tree then_label_expr = build1(LABEL_EXPR, void_type_node, then_label_decl); - append_statement(then_label_expr); - enter_scope(); - - visit_statements(branch.statements); - tree mapping = leave_scope(); - append_statement(mapping); - append_statement(goto_endif); - - tree else_label_expr = build1(LABEL_EXPR, void_type_node, else_label_decl); - append_statement(else_label_expr); - } - - void generic_visitor::visit(boot::import_declaration *) - { - } - - void generic_visitor::visit(boot::while_statement *statement) - { - location_t prerequisite_location = get_location(&statement->body().prerequisite().position()); - tree prerequisite_label_decl = build_label_decl("while_do", prerequisite_location); - auto prerequisite_label_expr = build1_loc(prerequisite_location, LABEL_EXPR, - void_type_node, prerequisite_label_decl); - auto goto_check = build1(GOTO_EXPR, void_type_node, prerequisite_label_decl); - tree branch_end_declaration = build_label_decl("while_end", UNKNOWN_LOCATION); - tree branch_end_expression = build1_loc(UNKNOWN_LOCATION, LABEL_EXPR, void_type_node, branch_end_declaration); - - append_statement(prerequisite_label_expr); - make_if_branch(statement->body(), goto_check); - - for (const auto branch : statement->branches) - { - make_if_branch(*branch, goto_check); - } - append_statement(branch_end_expression); - this->current_expression = NULL_TREE; - } - - void generic_visitor::visit_statements(const std::vector& statements) - { - for (boot::statement *const statement : statements) - { - statement->accept(this); - - if (this->current_expression != NULL_TREE && this->current_expression != error_mark_node) - { - append_statement(this->current_expression); - this->current_expression = NULL_TREE; - } - } - } - - void generic_visitor::visit(boot::return_statement *statement) - { - boot::expression *return_expression = &statement->return_expression(); - location_t statement_position = get_location(&statement->position()); - tree set_result{ NULL_TREE }; - tree return_type = TREE_TYPE(TREE_TYPE(current_function_decl)); - - if (TREE_THIS_VOLATILE(current_function_decl) == 1) - { - error_at(statement_position, "This procedure is not allowed to return"); - return; - } - if (return_expression != nullptr) - { - return_expression->accept(this); - - set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl), - this->current_expression); - } - if (return_type == void_type_node && set_result != NULL_TREE) - { - error_at(statement_position, "Proper procedure is not allowed to return a value"); - } - else if (return_type != void_type_node && set_result == NULL_TREE) - { - error_at(statement_position, "Procedure is expected to return a value of type '%s'", - print_type(return_type).c_str()); - } - else if (return_type != void_type_node && !is_assignable_from(return_type, this->current_expression)) - { - error_at(statement_position, "Cannot return '%s' from a procedure returning '%s'", - print_type(return_type).c_str(), - print_type(TREE_TYPE(this->current_expression)).c_str()); - } - else - { - tree return_stmt = build1_loc(statement_position, RETURN_EXPR, void_type_node, set_result); - append_statement(return_stmt); - } - this->current_expression = NULL_TREE; - } - - void generic_visitor::visit(boot::defer_statement *statement) - { - enter_scope(); - visit_statements(statement->statements); - defer(leave_scope()); - } - - void generic_visitor::visit(boot::case_statement *statement) - { - statement->condition().accept(this); - tree condition_expression = this->current_expression; - tree unqualified_condition = get_qualified_type(TREE_TYPE(this->current_expression), TYPE_UNQUALIFIED); - - if (!INTEGRAL_TYPE_P(unqualified_condition)) - { - error_at(get_location(&statement->condition().position()), - "Case expressions can only be integral numbers, characters and enumerations, given '%s'", - print_type(unqualified_condition).c_str()); - this->current_expression = NULL_TREE; - return; - } - tree end_label_declaration = create_artificial_label(get_location(&statement->position())); - tree switch_statements = alloc_stmt_list(); - - for (const boot::switch_case& case_block : statement->cases) - { - for (boot::expression *const case_label : case_block.labels) - { - case_label->accept(this); - location_t case_location = get_location(&case_label->position()); - - if (assert_constant(case_location) - && !is_assignable_from(unqualified_condition, this->current_expression)) - { - error_at(case_location, "Case type '%s' does not match the expression type '%s'", - print_type(TREE_TYPE(this->current_expression)).c_str(), - print_type(unqualified_condition).c_str()); - this->current_expression = error_mark_node; - } - tree case_label_declaration = create_artificial_label(case_location); - tree case_expression = build_case_label(this->current_expression, NULL_TREE, case_label_declaration); - - append_to_statement_list(case_expression, &switch_statements); - } - enter_scope(); - visit_statements(case_block.statements); - append_to_statement_list(leave_scope(), &switch_statements); - tree goto_end = build1(GOTO_EXPR, void_type_node, end_label_declaration); - - append_to_statement_list(goto_end, &switch_statements); - TREE_USED(end_label_declaration) = 1; - } - if (statement->alternative != nullptr) - { - tree case_label_declaration = create_artificial_label(UNKNOWN_LOCATION); - tree case_expression = build_case_label(NULL_TREE, NULL_TREE, case_label_declaration); - - append_to_statement_list(case_expression, &switch_statements); - - enter_scope(); - visit_statements(*statement->alternative); - append_to_statement_list(leave_scope(), &switch_statements); - - TREE_USED(end_label_declaration) = 1; - } - tree switch_expression = build2(SWITCH_EXPR, TREE_TYPE(condition_expression), - condition_expression, switch_statements); - - append_statement(switch_expression); - - tree end_label_expression = build1(LABEL_EXPR, void_type_node, end_label_declaration); - append_statement(end_label_expression); - - this->current_expression = NULL_TREE; - } - - bool generic_visitor::assert_constant(location_t expression_location) - { - tree constant_expression = extract_constant(this->current_expression); - - if (constant_expression == NULL_TREE) - { - error_at(expression_location, "Expected a constant expression"); - this->current_expression = error_mark_node; - } - else - { - this->current_expression = constant_expression; - } - return this->current_expression != error_mark_node; - } -} diff --git a/gcc/elna-spec.cc b/gcc/elna-spec.cc deleted file mode 100644 index 5d1ace1..0000000 --- a/gcc/elna-spec.cc +++ /dev/null @@ -1,31 +0,0 @@ -/* Specific flags and argument handling of the Elna front end. - Copyright (C) 2025 Free Software Foundation, Inc. - -GCC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. - -GCC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -. */ - -void lang_specific_driver(struct cl_decoded_option ** /* in_decoded_options */, - unsigned int * /* in_decoded_options_count */, - int * /*in_added_libraries */) -{ -} - -/* Called before linking. Returns 0 on success and -1 on failure. */ -int lang_specific_pre_link(void) -{ - return 0; -} - -/* Number of extra output files that lang_specific_pre_link may generate. */ -int lang_specific_extra_outfiles = 0; diff --git a/gcc/elna-tree.cc b/gcc/elna-tree.cc deleted file mode 100644 index de7f6b0..0000000 --- a/gcc/elna-tree.cc +++ /dev/null @@ -1,315 +0,0 @@ -/* Utilities to manipulate GCC trees. - Copyright (C) 2025 Free Software Foundation, Inc. - -GCC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. - -GCC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -. */ - -#include "elna/gcc/elna-tree.h" -#include "elna/gcc/elna-diagnostic.h" -#include "elna/gcc/elna1.h" - -#include "function.h" -#include "stor-layout.h" -#include "diagnostic-core.h" - -namespace elna::gcc -{ - bool is_integral_type(tree type) - { - gcc_assert(TYPE_P(type)); - return TREE_CODE(type) == INTEGER_TYPE && type != elna_char_type_node; - } - - bool is_numeric_type(tree type) - { - return is_integral_type(type) || type == elna_float_type_node; - } - - bool is_unique_type(tree type) - { - gcc_assert(TYPE_P(type)); - return RECORD_OR_UNION_TYPE_P(type) || TREE_CODE(type) == ENUMERAL_TYPE; - } - - bool is_void_type(tree type) - { - return type == NULL_TREE || type == void_type_node; - } - - bool is_castable_type(tree type) - { - gcc_assert(TYPE_P(type)); - return INTEGRAL_TYPE_P(type) || POINTER_TYPE_P(type) || TREE_CODE(type) == REAL_TYPE; - } - - bool are_compatible_pointers(tree lhs_type, tree rhs) - { - gcc_assert(TYPE_P(lhs_type)); - tree rhs_type = TREE_TYPE(rhs); - - return (POINTER_TYPE_P(lhs_type) && rhs == elna_pointer_nil_node) - || (POINTER_TYPE_P(lhs_type) && lhs_type == rhs_type); - } - - tree prepare_rvalue(tree rvalue) - { - if (DECL_P(rvalue) && TREE_CODE(TREE_TYPE(rvalue)) == FUNCTION_TYPE) - { - return build1(ADDR_EXPR, build_pointer_type_for_mode(TREE_TYPE(rvalue), VOIDmode, true), rvalue); - } - else - { - return rvalue; - } - } - - bool is_assignable_from(tree assignee, tree assignment) - { - return get_qualified_type(TREE_TYPE(assignment), TYPE_UNQUALIFIED) == assignee - || are_compatible_pointers(assignee, assignment); - } - - void append_statement(tree statement_tree) - { - if (!vec_safe_is_empty(f_binding_level->defers)) - { - append_to_statement_list(statement_tree, &f_binding_level->defers->begin()->try_statements); - } - else - { - append_to_statement_list(statement_tree, &f_binding_level->statement_list); - } - } - - void defer(tree statement_tree) - { - defer_scope new_defer{ statement_tree, alloc_stmt_list() }; - vec_safe_insert(f_binding_level->defers, 0, new_defer); - } - - tree chain_defer() - { - if (vec_safe_is_empty(f_binding_level->defers)) - { - return f_binding_level->statement_list; - } - defer_scope *defer_iterator = f_binding_level->defers->begin(); - tree defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, - defer_iterator->try_statements, defer_iterator->defer_block); - int i; - - FOR_EACH_VEC_ELT_FROM(*f_binding_level->defers, i, defer_iterator, 1) - { - append_to_statement_list(defer_tree, &defer_iterator->try_statements); - defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, - defer_iterator->try_statements, defer_iterator->defer_block); - } - return build2(COMPOUND_EXPR, TREE_TYPE(defer_tree), f_binding_level->statement_list, defer_tree); - } - - tree build_field(location_t location, tree record_type, const std::string name, tree type) - { - tree field_declaration = build_decl(location, - FIELD_DECL, get_identifier(name.c_str()), type); - TREE_ADDRESSABLE(field_declaration) = 1; - DECL_CONTEXT(field_declaration) = record_type; - - return field_declaration; - } - - tree do_pointer_arithmetic(boot::binary_operator binary_operator, - tree left, tree right, location_t operation_location) - { - tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED); - tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED); - if (binary_operator == boot::binary_operator::sum) - { - tree pointer{ NULL_TREE }; - tree offset{ NULL_TREE }; - tree pointer_type{ NULL_TREE }; - - if (POINTER_TYPE_P(left_type) && is_integral_type(right_type)) - { - pointer = left; - offset = right; - pointer_type = left_type; - } - else if (is_integral_type(left_type) && POINTER_TYPE_P(right_type)) - { - pointer = right; - offset = left; - pointer_type = right_type; - } - else - { - return error_mark_node; - } - tree size_exp = pointer_type == elna_pointer_type_node - ? size_one_node - : 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_loc(operation_location, POINTER_PLUS_EXPR, TREE_TYPE(pointer), pointer, offset); - } - else if (binary_operator == boot::binary_operator::subtraction) - { - if (POINTER_TYPE_P(left_type) && is_integral_type(right_type)) - { - tree pointer_type = left_type; - tree offset_type = right_type; - 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_loc(operation_location, POINTER_PLUS_EXPR, pointer_type, left, convert_expression); - } - else if (POINTER_TYPE_P(left_type) && POINTER_TYPE_P(right_type) && left_type == right_type) - { - return fold_build2_loc(operation_location, POINTER_DIFF_EXPR, ssizetype, left, right); - } - } - gcc_unreachable(); - } - - tree build_binary_operation(bool condition, boot::binary_expression *expression, - tree_code operator_code, tree left, tree right, tree target_type) - { - location_t expression_location = get_location(&expression->position()); - tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED); - tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED); - - if (condition) - { - return fold_build2_loc(expression_location, operator_code, target_type, left, right); - } - else - { - error_at(expression_location, - "invalid operands of type '%s' and '%s' for operator %s", - print_type(left_type).c_str(), print_type(right_type).c_str(), - boot::print_binary_operator(expression->operation())); - return error_mark_node; - } - } - - tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name) - { - if (type == error_mark_node) - { - return type; - } - tree field_declaration = TYPE_FIELDS(type); - - if (!RECORD_OR_UNION_TYPE_P(type)) - { - error_at(expression_location, "Type '%s' does not have a field named '%s'", - print_type(type).c_str(), field_name.c_str()); - return error_mark_node; - } - while (field_declaration != NULL_TREE) - { - tree declaration_name = DECL_NAME(field_declaration); - const char *identifier_pointer = IDENTIFIER_POINTER(declaration_name); - - if (field_name == identifier_pointer) - { - break; - } - field_declaration = TREE_CHAIN(field_declaration); - } - if (field_declaration == NULL_TREE) - { - error_at(expression_location, "Aggregate type does not have a field '%s'", field_name.c_str()); - return error_mark_node; - } - return field_declaration; - } - - tree build_global_pointer_type(tree type) - { - return build_pointer_type_for_mode(type, VOIDmode, true); - } - - tree build_static_array_type(tree type, const std::uint64_t size) - { - tree upper_bound = build_int_cst_type(integer_type_node, size); - tree range_type = build_range_type(integer_type_node, size_one_node, upper_bound); - - return build_array_type(type, range_type); - } - - tree build_enumeration_type(const std::vector& members) - { - tree composite_type_node = make_node(ENUMERAL_TYPE); - const tree base_type = integer_type_node; - - TREE_TYPE(composite_type_node) = base_type; - ENUM_IS_SCOPED(composite_type_node) = 1; - - tree *pp = &TYPE_VALUES(composite_type_node); - std::size_t order{ 1 }; - - for (const std::string& member : members) - { - tree member_name = get_identifier(member.c_str()); - tree member_declaration = build_decl(UNKNOWN_LOCATION, CONST_DECL, member_name, composite_type_node); - - DECL_CONTEXT(member_declaration) = composite_type_node; - DECL_INITIAL(member_declaration) = build_int_cst_type(composite_type_node, order++); - TREE_CONSTANT(member_declaration) = 1; - TREE_READONLY(member_declaration) = 1; - - TYPE_MAX_VALUE(composite_type_node) = DECL_INITIAL(member_declaration); - - *pp = build_tree_list(member_name, member_declaration); - pp = &TREE_CHAIN(*pp); - } - TYPE_MIN_VALUE(composite_type_node) = DECL_INITIAL(TREE_VALUE(TYPE_VALUES(composite_type_node))); - TYPE_UNSIGNED(composite_type_node) = TYPE_UNSIGNED(base_type); - SET_TYPE_ALIGN(composite_type_node, TYPE_ALIGN(base_type)); - TYPE_SIZE(composite_type_node) = NULL_TREE; - TYPE_PRECISION(composite_type_node) = TYPE_PRECISION(base_type); - - layout_type(composite_type_node); - return composite_type_node; - } - - tree build_label_decl(const char *name, location_t loc) - { - auto label_decl = build_decl(loc, LABEL_DECL, get_identifier(name), void_type_node); - - DECL_CONTEXT(label_decl) = current_function_decl; - - return label_decl; - } - - tree extract_constant(tree expression) - { - int code = TREE_CODE(expression); - - if (code == CONST_DECL) - { - return DECL_INITIAL(expression); - } - else if (TREE_CODE_CLASS(code) == tcc_constant) - { - return expression; - } - return NULL_TREE; - } -} diff --git a/gcc/elna1.cc b/gcc/elna1.cc deleted file mode 100644 index 0333f70..0000000 --- a/gcc/elna1.cc +++ /dev/null @@ -1,256 +0,0 @@ -/* Language-dependent hooks for Elna. - Copyright (C) 2025 Free Software Foundation, Inc. - -GCC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. - -GCC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -. */ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "target.h" -#include "function.h" -#include "tree.h" -#include "elna/gcc/elna1.h" -#include "diagnostic.h" -#include "opts.h" -#include "debug.h" -#include "langhooks.h" -#include "langhooks-def.h" - -#include -#include "elna/boot/dependency.h" -#include "elna/gcc/elna-tree.h" -#include "elna/gcc/elna-generic.h" -#include "elna/gcc/elna-diagnostic.h" -#include "elna/gcc/elna-builtins.h" - -tree elna_global_trees[ELNA_TI_MAX]; -hash_map *elna_global_decls = nullptr; - -/* The resulting tree type. */ - -union GTY ((desc("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"), - chain_next("CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), " - "TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN " - "(&%h.generic)) : NULL"))) lang_tree_node -{ - union tree_node GTY ((tag ("0"), desc ("tree_node_structure (&%h)"))) generic; -}; - -/* Language hooks. */ - -static bool elna_langhook_init(void) -{ - build_common_tree_nodes(false); - - elna::gcc::init_ttree(); - elna_global_decls = hash_map::create_ggc(default_hash_map_size); - - build_common_builtin_nodes(); - - return true; -} - -using dependency_state = elna::boot::dependency_state>; - -static elna::boot::dependency elna_parse_file(dependency_state& state, const char *filename) -{ - std::ifstream entry_point{ filename, std::ios::in }; - - if (!entry_point) - { - fatal_error(UNKNOWN_LOCATION, "Cannot open filename %s: %m", filename); - } - elna::gcc::linemap_guard{ filename }; - elna::boot::dependency outcome = elna::boot::read_source(entry_point, filename); - - if (outcome.has_errors()) - { - elna::gcc::report_errors(outcome.errors()); - return outcome; - } - elna::boot::symbol_bag outcome_bag = elna::boot::symbol_bag{ std::move(outcome.unresolved), state.globals }; - - for (const auto& sub_tree : outcome.tree->imports) - { - std::filesystem::path sub_path = "source" / elna::boot::build_path(sub_tree->segments); - std::unordered_map::const_iterator cached_import = - state.cache.find(sub_path); - - if (cached_import == state.cache.end()) - { - elna_parse_file(state, sub_path.c_str()); - cached_import = state.cache.find(sub_path); - } - outcome_bag.add_import(cached_import->second); - } - outcome.errors() = analyze_semantics(filename, outcome.tree, outcome_bag); - - if (outcome.has_errors()) - { - elna::gcc::report_errors(outcome.errors()); - return outcome; - } - state.cache.insert({ filename, outcome_bag }); - elna::gcc::rewrite_symbol_table(outcome_bag.leave(), state.custom); - - return outcome; -} - -static void elna_langhook_parse_file(void) -{ - dependency_state state{ elna::gcc::builtin_symbol_table() }; - - for (unsigned int i = 0; i < num_in_fnames; i++) - { - elna::boot::dependency outcome = elna_parse_file(state, in_fnames[i]); - - linemap_add(line_table, LC_ENTER, 0, in_fnames[i], 1); - elna::gcc::generic_visitor generic_visitor{ state.custom, state.cache.find(in_fnames[i])->second }; - outcome.tree->accept(&generic_visitor); - linemap_add(line_table, LC_LEAVE, 0, NULL, 0); - } -} - -static tree elna_langhook_type_for_mode(enum machine_mode mode, int unsignedp) -{ - if (mode == TYPE_MODE(float_type_node)) - { - return float_type_node; - } - else if (mode == TYPE_MODE(double_type_node)) - { - return double_type_node; - } - if (mode == TYPE_MODE(intQI_type_node)) - { - return unsignedp ? unsigned_intQI_type_node : intQI_type_node; - } - else if (mode == TYPE_MODE(intHI_type_node)) - { - return unsignedp ? unsigned_intHI_type_node : intHI_type_node; - } - else if (mode == TYPE_MODE(intSI_type_node)) - { - return unsignedp ? unsigned_intSI_type_node : intSI_type_node; - } - else if (mode == TYPE_MODE(intDI_type_node)) - { - return unsignedp ? unsigned_intDI_type_node : intDI_type_node; - } - else if (mode == TYPE_MODE(intTI_type_node)) - { - return unsignedp ? unsigned_intTI_type_node : intTI_type_node; - } - else if (mode == TYPE_MODE(integer_type_node)) - { - return unsignedp ? unsigned_type_node : integer_type_node; - } - else if (mode == TYPE_MODE(long_integer_type_node)) - { - return unsignedp ? long_unsigned_type_node : long_integer_type_node; - } - else if (mode == TYPE_MODE(long_long_integer_type_node)) - { - return unsignedp - ? long_long_unsigned_type_node - : long_long_integer_type_node; - } - if (COMPLEX_MODE_P(mode)) - { - if (mode == TYPE_MODE(complex_float_type_node)) - { - return complex_float_type_node; - } - if (mode == TYPE_MODE(complex_double_type_node)) - { - return complex_double_type_node; - } - if (mode == TYPE_MODE(complex_long_double_type_node)) - { - return complex_long_double_type_node; - } - if (mode == TYPE_MODE(complex_integer_type_node) && !unsignedp) - { - return complex_integer_type_node; - } - } - /* gcc_unreachable */ - return nullptr; -} - -static bool global_bindings_p(void) -{ - return current_function_decl == NULL_TREE; -} - -static tree pushdecl(tree decl) -{ - return decl; -} - -static tree elna_langhook_builtin_function(tree decl) -{ - elna_global_decls->put(IDENTIFIER_POINTER(DECL_NAME(decl)), decl); - return decl; -} - -static unsigned int elna_langhook_option_lang_mask(void) -{ - return CL_Elna; -} - -/* Creates an expression whose value is that of EXPR, converted to type TYPE. - This function implements all reasonable scalar conversions. */ -tree convert(tree type, tree expr) -{ - if (error_operand_p(type) || error_operand_p(expr)) - { - return error_mark_node; - } - if (TREE_TYPE(expr) == type) - { - return expr; - } - return error_mark_node; -} - -#undef LANG_HOOKS_NAME -#define LANG_HOOKS_NAME "GNU Elna" - -#undef LANG_HOOKS_INIT -#define LANG_HOOKS_INIT elna_langhook_init - -#undef LANG_HOOKS_PARSE_FILE -#define LANG_HOOKS_PARSE_FILE elna_langhook_parse_file - -#undef LANG_HOOKS_TYPE_FOR_MODE -#define LANG_HOOKS_TYPE_FOR_MODE elna_langhook_type_for_mode - -#undef LANG_HOOKS_GETDECLS -#define LANG_HOOKS_GETDECLS hook_tree_void_null - -#undef LANG_HOOKS_BUILTIN_FUNCTION -#define LANG_HOOKS_BUILTIN_FUNCTION elna_langhook_builtin_function - -#undef LANG_HOOKS_IDENTIFIER_SIZE -#define LANG_HOOKS_IDENTIFIER_SIZE sizeof(struct tree_identifier) - -#undef LANG_HOOKS_OPTION_LANG_MASK -#define LANG_HOOKS_OPTION_LANG_MASK elna_langhook_option_lang_mask - -struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; - -#include "gt-elna-elna1.h" -#include "gtype-elna.h" diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc new file mode 100644 index 0000000..7c97027 --- /dev/null +++ b/gcc/gcc/elna-builtins.cc @@ -0,0 +1,274 @@ +/* Builtin definitions. + Copyright (C) 2025 Free Software Foundation, Inc. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include + +#include "elna/gcc/elna-builtins.h" +#include "elna/gcc/elna1.h" +#include "stor-layout.h" +#include "stringpool.h" +#include "elna/gcc/elna-tree.h" + +namespace elna::gcc +{ + void init_ttree() + { + elna_int_type_node = long_integer_type_node; + elna_word_type_node = size_type_node; + elna_char_type_node = unsigned_char_type_node; + elna_pointer_type_node = ptr_type_node; + elna_float_type_node = double_type_node; + + elna_bool_type_node = boolean_type_node; + elna_bool_true_node = boolean_true_node; + elna_bool_false_node = boolean_false_node; + + elna_pointer_nil_node = null_pointer_node; + + elna_string_type_node = make_node(RECORD_TYPE); + tree string_ptr_type = build_pointer_type_for_mode(elna_char_type_node, VOIDmode, true); + + elna_string_length_field_node = build_field(UNKNOWN_LOCATION, + elna_string_type_node, "length", build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST)); + elna_string_ptr_field_node = build_field(UNKNOWN_LOCATION, + elna_string_type_node, "ptr", build_qualified_type(string_ptr_type, TYPE_QUAL_CONST)); + + TYPE_FIELDS(elna_string_type_node) = chainon(elna_string_ptr_field_node, elna_string_length_field_node); + layout_type(elna_string_type_node); + } + + static + tree declare_builtin_type(std::shared_ptr symbol_table, const char *name, tree type) + { + tree identifier = get_identifier(name); + tree type_declaration = build_decl(UNKNOWN_LOCATION, TYPE_DECL, identifier, type); + + symbol_table->enter(name, type_declaration); + + return type_declaration; + } + + std::shared_ptr builtin_symbol_table() + { + auto builtin_table = std::make_shared(); + + declare_builtin_type(builtin_table, "Int", elna_int_type_node); + declare_builtin_type(builtin_table, "Word", elna_word_type_node); + declare_builtin_type(builtin_table, "Char", elna_char_type_node); + declare_builtin_type(builtin_table, "Bool", elna_bool_type_node); + declare_builtin_type(builtin_table, "Pointer", elna_pointer_type_node); + declare_builtin_type(builtin_table, "Float", elna_float_type_node); + + tree string_declaration = declare_builtin_type(builtin_table, "String", elna_string_type_node); + TYPE_NAME(elna_string_type_node) = DECL_NAME(string_declaration); + TYPE_STUB_DECL(elna_string_type_node) = string_declaration; + + return builtin_table; + } + + tree build_composite_type(const std::vector& fields, tree composite_type_node, + std::shared_ptr symbols) + { + for (auto& field : fields) + { + tree rewritten_field = get_inner_alias(field.second, symbols); + tree field_declaration = build_field(UNKNOWN_LOCATION, + composite_type_node, field.first, rewritten_field); + TYPE_FIELDS(composite_type_node) = chainon(TYPE_FIELDS(composite_type_node), field_declaration); + } + layout_type(composite_type_node); + return composite_type_node; + } + + tree build_procedure_type(const boot::procedure_type& procedure, std::shared_ptr symbols) + { + std::vector parameter_types(procedure.parameters.size()); + + for (std::size_t i = 0; i < procedure.parameters.size(); ++i) + { + parameter_types[i] = get_inner_alias(procedure.parameters.at(i), symbols); + } + tree return_type = void_type_node; + + if (!procedure.return_type.proper_type.empty()) + { + return_type = get_inner_alias(procedure.return_type.proper_type, symbols); + } + return build_function_type_array(return_type, procedure.parameters.size(), parameter_types.data()); + } + + tree get_inner_alias(const boot::type& type, std::shared_ptr symbols) + { + if (auto reference = type.get()) + { + auto looked_up = symbols->lookup(reference->identifier); + gcc_assert(looked_up != NULL_TREE); + + return TREE_TYPE(looked_up); + } + else if (auto reference = type.get()) + { + tree composite_type_node = make_node(RECORD_TYPE); + + build_composite_type(reference->fields, composite_type_node, symbols); + + return composite_type_node; + } + else if (auto reference = type.get()) + { + tree composite_type_node = make_node(UNION_TYPE); + + build_composite_type(reference->fields, composite_type_node, symbols); + + return composite_type_node; + } + else if (auto reference = type.get()) + { + return build_enumeration_type(reference->members); + } + else if (auto reference = type.get()) + { + return build_global_pointer_type(get_inner_alias(reference->base, symbols)); + } + else if (auto reference = type.get()) + { + tree base = get_inner_alias(reference->base, symbols); + + return build_static_array_type(base, reference->size); + } + else if (auto reference = type.get()) + { + auto procedure = build_procedure_type(*reference, symbols); + + return build_global_pointer_type(procedure); + } + else if (auto reference = type.get()) + { + return TREE_TYPE(handle_symbol(reference->name, reference, symbols)); + } + return error_mark_node; + } + + tree handle_symbol(const std::string& symbol_name, std::shared_ptr reference, + std::shared_ptr symbols) + { + tree looked_up = symbols->lookup(symbol_name); + + if (looked_up == NULL_TREE) + { + tree type_tree = get_inner_alias(reference->reference, symbols); + looked_up = build_decl(UNKNOWN_LOCATION, TYPE_DECL, + get_identifier(symbol_name.c_str()), type_tree); + + TREE_PUBLIC(looked_up) = 1; + if (is_unique_type(type_tree)) + { + TYPE_NAME(type_tree) = DECL_NAME(looked_up); + TYPE_STUB_DECL(type_tree) = looked_up; + } + else + { + TYPE_NAME(type_tree) = looked_up; + } + symbols->enter(symbol_name, looked_up); + } + return looked_up; + } + + void declare_procedure(const std::string& name, const boot::procedure_info& info, + std::shared_ptr symbols) + { + tree declaration_type = gcc::build_procedure_type(info.symbol, symbols); + tree fndecl = build_fn_decl(name.c_str(), declaration_type); + symbols->enter(name, fndecl); + + if (info.symbol.return_type.no_return) + { + TREE_THIS_VOLATILE(fndecl) = 1; + } + tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, TREE_TYPE(declaration_type)); + DECL_CONTEXT(resdecl) = fndecl; + DECL_RESULT(fndecl) = resdecl; + + tree argument_chain = NULL_TREE; + function_args_iterator parameter_type; + function_args_iter_init(¶meter_type, declaration_type); + + std::vector::const_iterator parameter_name = info.names.cbegin(); + + for (boot::type parameter : info.symbol.parameters) + { + tree declaration_tree = build_decl(UNKNOWN_LOCATION, PARM_DECL, + get_identifier(parameter_name->c_str()), function_args_iter_cond(¶meter_type)); + DECL_CONTEXT(declaration_tree) = fndecl; + DECL_ARG_TYPE(declaration_tree) = function_args_iter_cond(¶meter_type); + + argument_chain = chainon(argument_chain, declaration_tree); + function_args_iter_next(¶meter_type); + ++parameter_name; + } + DECL_ARGUMENTS(fndecl) = argument_chain; + TREE_ADDRESSABLE(fndecl) = 1; + DECL_EXTERNAL(fndecl) = info.is_extern(); + TREE_PUBLIC(fndecl) = info.exported; + } + + tree declare_variable(const std::string& name, const boot::variable_info& info, + std::shared_ptr symbols) + { + auto variable_type = get_inner_alias(info.symbol, symbols); + tree declaration_tree = build_decl(UNKNOWN_LOCATION, VAR_DECL, get_identifier(name.c_str()), variable_type); + + TREE_ADDRESSABLE(declaration_tree) = 1; + DECL_EXTERNAL(declaration_tree) = info.is_extern; + TREE_PUBLIC(declaration_tree) = info.exported; + + symbols->enter(name, declaration_tree); + + return declaration_tree; + } + + void declare_type(const std::string& name, const boot::type_info& info, std::shared_ptr symbols) + { + // The top level symbol table has basic (builtin) types in it which are not aliases. + if (auto alias_type = info.symbol.get()) + { + tree type_declaration = handle_symbol(name, alias_type, symbols); + + TREE_PUBLIC(type_declaration) = info.exported; + } + } + + void rewrite_symbol_table(std::shared_ptr info_table, std::shared_ptr symbols) + { + for (auto& [symbol_name, symbol_info] : *info_table) + { + if (auto type_info = symbol_info->is_type()) + { + declare_type(symbol_name, *type_info, symbols); + } + else if (auto variable_info = symbol_info->is_variable()) + { + declare_variable(symbol_name, *variable_info, symbols); + } + else if (auto procedure_info = symbol_info->is_procedure()) + { + declare_procedure(symbol_name, *procedure_info, symbols); + } + } + } +} diff --git a/gcc/gcc/elna-diagnostic.cc b/gcc/gcc/elna-diagnostic.cc new file mode 100644 index 0000000..fa32788 --- /dev/null +++ b/gcc/gcc/elna-diagnostic.cc @@ -0,0 +1,167 @@ +/* Elna frontend specific diagnostic routines. + Copyright (C) 2025 Free Software Foundation, Inc. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "elna/gcc/elna-diagnostic.h" +#include "elna/gcc/elna-tree.h" +#include "elna/gcc/elna1.h" + +namespace elna::gcc +{ + linemap_guard::linemap_guard(const char *filename) + { + linemap_add(line_table, LC_ENTER, 0, filename, 1); + } + + linemap_guard::~linemap_guard() + { + linemap_add(line_table, LC_LEAVE, 0, NULL, 0); + } + + location_t get_location(const boot::position *position) + { + linemap_line_start(line_table, position->line, 0); + + return linemap_position_for_column(line_table, position->column); + } + + std::string print_aggregate_name(tree type, const std::string& kind_name) + { + if (TYPE_IDENTIFIER(type) == NULL_TREE) + { + return kind_name; + } + else + { + return std::string(IDENTIFIER_POINTER(TYPE_IDENTIFIER(type))); + } + } + + std::string print_type(tree type) + { + gcc_assert(TYPE_P(type)); + + tree unqualified_type = get_qualified_type(type, TYPE_UNQUALIFIED); + tree_code code = TREE_CODE(type); + + if (unqualified_type == elna_int_type_node) + { + return "Int"; + } + else if (unqualified_type == elna_word_type_node) + { + return "Word"; + } + else if (unqualified_type == elna_bool_type_node) + { + return "Bool"; + } + else if (unqualified_type == elna_pointer_type_node) + { + return "Pointer"; + } + else if (unqualified_type == elna_float_type_node) + { + return "Float"; + } + else if (unqualified_type == elna_char_type_node) + { + return "Char"; + } + else if (unqualified_type == elna_string_type_node) + { + return "String"; + } + else if (is_void_type(unqualified_type)) // For procedures without a return type. + { + return "()"; + } + else if (POINTER_TYPE_P(unqualified_type)) + { + tree pointer_target_type = TREE_TYPE(type); + + if (TREE_CODE(pointer_target_type) == FUNCTION_TYPE) + { + return print_type(pointer_target_type); + } + else + { + return std::string("^" + print_type(pointer_target_type)); + } + } + else if (code == FUNCTION_TYPE) + { + std::string output = "proc("; + tree parameter_type = TYPE_ARG_TYPES(type); + while (TREE_VALUE(parameter_type) != void_type_node) + { + output += print_type(TREE_VALUE(parameter_type)); + parameter_type = TREE_CHAIN(parameter_type); + if (TREE_VALUE(parameter_type) == void_type_node) + { + break; + } + else + { + output += ", "; + } + } + output += ')'; + tree return_type = TREE_TYPE(type); + + if (!is_void_type(return_type)) + { + output += " -> " + print_type(return_type); + } + return output; + } + else if (code == ARRAY_TYPE) + { + return "array"; + } + else if (code == RECORD_TYPE) + { + return print_aggregate_name(unqualified_type, "record"); + } + else if (code == UNION_TYPE) + { + return print_aggregate_name(unqualified_type, "union"); + } + else if (code == ENUMERAL_TYPE) + { + return print_aggregate_name(unqualified_type, "enumeration"); + } + else + { + return "<>"; + } + gcc_unreachable(); + } + + void report_errors(const std::deque>& errors) + { + for (const auto& error : errors) + { + location_t gcc_location{ UNKNOWN_LOCATION }; + + if (error->position.line != 0 || error->position.column != 0) + { + gcc_location = get_location(&error->position); + } + error_at(gcc_location, error->what().c_str()); + } + } +} diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc new file mode 100644 index 0000000..66bd9a2 --- /dev/null +++ b/gcc/gcc/elna-generic.cc @@ -0,0 +1,1277 @@ +/* Visitor generating a GENERIC tree. + Copyright (C) 2025 Free Software Foundation, Inc. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include + +#include "elna/gcc/elna-generic.h" +#include "elna/gcc/elna-diagnostic.h" +#include "elna/gcc/elna1.h" +#include "elna/gcc/elna-builtins.h" + +#include "ggc.h" +#include "function.h" +#include "cgraph.h" +#include "gimplify.h" +#include "stringpool.h" +#include "diagnostic.h" +#include "realmpfr.h" +#include "varasm.h" +#include "fold-const.h" +#include "langhooks.h" + +namespace elna::gcc +{ + generic_visitor::generic_visitor(std::shared_ptr symbol_table, boot::symbol_bag bag) + : bag(bag), symbols(symbol_table) + { + } + + void generic_visitor::build_procedure_call(location_t call_location, + tree procedure_address, const std::vector& arguments) + { + vec *argument_trees = nullptr; + tree symbol_type = TREE_TYPE(TREE_TYPE(procedure_address)); + + tree current_parameter = TYPE_ARG_TYPES(symbol_type); + + vec_alloc(argument_trees, arguments.size()); + for (boot::expression *const argument : arguments) + { + location_t argument_location = get_location(&argument->position()); + if (VOID_TYPE_P(TREE_VALUE(current_parameter))) + { + error_at(argument_location, "Too many arguments, expected %i, got %lu", + list_length(TYPE_ARG_TYPES(symbol_type)) - 1, arguments.size()); + this->current_expression = error_mark_node; + break; + } + argument->accept(this); + this->current_expression = prepare_rvalue(this->current_expression); + if (!is_assignable_from(TREE_VALUE(current_parameter), this->current_expression)) + { + error_at(argument_location, + "Cannot assign value of type '%s' to variable of type '%s'", + print_type(TREE_TYPE(this->current_expression)).c_str(), + print_type(TREE_VALUE(current_parameter)).c_str()); + this->current_expression = error_mark_node; + } + current_parameter = TREE_CHAIN(current_parameter); + argument_trees->quick_push(this->current_expression); + } + tree stmt = fold_build_call_array_loc(call_location, TREE_TYPE(symbol_type), + procedure_address, vec_safe_length(argument_trees), vec_safe_address(argument_trees)); + + if (!VOID_TYPE_P(TREE_VALUE(current_parameter))) + { + error_at(call_location, "Too few arguments, expected %i, got %lu", + list_length(TYPE_ARG_TYPES(symbol_type)) - 1, arguments.size()); + this->current_expression = error_mark_node; + } + else + { + this->current_expression = stmt; + } + } + + void generic_visitor::build_record_call(location_t call_location, + tree symbol, const std::vector& arguments) + { + vec *tree_arguments = nullptr; + tree record_fields = TYPE_FIELDS(symbol); + for (boot::expression *const argument : arguments) + { + location_t argument_location = get_location(&argument->position()); + + if (is_void_type(record_fields)) + { + error_at(argument_location, "Too many arguments, expected %i, got %lu", + list_length(TYPE_FIELDS(symbol)), arguments.size()); + this->current_expression = error_mark_node; + break; + } + argument->accept(this); + tree unqualified_field = get_qualified_type(TREE_TYPE(record_fields), TYPE_UNQUALIFIED); + if (!is_assignable_from(unqualified_field, this->current_expression)) + { + error_at(argument_location, + "Cannot assign value of type '%s' to variable of type '%s'", + print_type(TREE_TYPE(this->current_expression)).c_str(), + print_type(TREE_TYPE(record_fields)).c_str()); + this->current_expression = error_mark_node; + } + CONSTRUCTOR_APPEND_ELT(tree_arguments, record_fields, this->current_expression); + record_fields = TREE_CHAIN(record_fields); + } + if (!is_void_type(record_fields)) + { + error_at(call_location, "Too few arguments, expected %i, got %lu", + list_length(TYPE_FIELDS(symbol)), arguments.size()); + this->current_expression = error_mark_node; + } + else + { + this->current_expression = build_constructor(symbol, tree_arguments); + } + } + + void generic_visitor::build_assert_builtin(location_t call_location, + const std::vector& arguments) + { + if (arguments.size() != 1) + { + error_at(call_location, "assert expects exactly one boolean argument, got %lu", arguments.size()); + this->current_expression = error_mark_node; + } + else + { + arguments.at(0)->accept(this); + tree argument_type = TREE_TYPE(this->current_expression); + + if (argument_type != elna_bool_type_node) + { + error_at(call_location, "assert expects exactly one boolean argument, got %s", + print_type(argument_type).c_str()); + this->current_expression = error_mark_node; + } + tree constant_expression = extract_constant(this->current_expression); + if (constant_expression == boolean_false_node) + { + this->current_expression = call_built_in(call_location, "__builtin_unreachable", void_type_node); + } + else if (constant_expression != boolean_true_node) + { + tree assert_expression = call_built_in(call_location, "__builtin_trap", void_type_node); + this->current_expression = build3(COND_EXPR, void_type_node, this->current_expression, + NULL_TREE, assert_expression); + } + else + { + this->current_expression = NULL_TREE; + } + } + } + + bool generic_visitor::build_builtin_procedures(boot::procedure_call *call) + { + location_t call_location = get_location(&call->position()); + + if (boot::named_expression *named_call = call->callable().is_named()) + { + if (named_call->name == "assert") + { + build_assert_builtin(call_location, call->arguments); + return true; + } + } + return false; + } + + void generic_visitor::visit(boot::procedure_call *call) + { + if (build_builtin_procedures(call)) + { + return; + } + location_t call_location = get_location(&call->position()); + call->callable().accept(this); + + tree expression_type = TYPE_P(this->current_expression) + ? this->current_expression + : TREE_TYPE(this->current_expression); + + if (TREE_CODE(expression_type) == RECORD_TYPE) + { + build_record_call(call_location, expression_type, call->arguments); + } + else if (TREE_CODE(expression_type) == FUNCTION_TYPE) + { + this->current_expression = build1(ADDR_EXPR, + build_global_pointer_type(expression_type), this->current_expression); + build_procedure_call(call_location, this->current_expression, call->arguments); + } + else if (POINTER_TYPE_P(expression_type) && TREE_CODE(TREE_TYPE(expression_type)) == FUNCTION_TYPE) + { + build_procedure_call(call_location, this->current_expression, call->arguments); + } + else + { + error_at(call_location, "'%s' cannot be called, it is neither a procedure nor record", + print_type(expression_type).c_str()); + this->current_expression = error_mark_node; + } + } + + void generic_visitor::visit(boot::cast_expression *expression) + { + tree cast_target = get_inner_alias(expression->expression_type, this->symbols->scope()); + + expression->value().accept(this); + tree cast_source = TREE_TYPE(this->current_expression); + + if (is_castable_type(cast_target) && (is_castable_type(cast_source))) + { + this->current_expression = build1_loc(get_location(&expression->position()), CONVERT_EXPR, + cast_target, this->current_expression); + } + else + { + error_at(get_location(&expression->position()), "Type '%s' cannot be converted to '%s'", + print_type(cast_source).c_str(), print_type(cast_target).c_str()); + this->current_expression = error_mark_node; + } + } + + void generic_visitor::visit(boot::program *program) + { + visit(static_cast(program)); + + tree declaration_type = build_function_type_list(elna_int_type_node, + elna_int_type_node, + build_global_pointer_type(build_global_pointer_type(elna_char_type_node)), + NULL_TREE); + tree fndecl = build_fn_decl("main", declaration_type); + + tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, integer_type_node); + DECL_CONTEXT(resdecl) = fndecl; + DECL_RESULT(fndecl) = resdecl; + + push_struct_function(fndecl, false); + DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc(); + + enter_scope(); + + tree parameter_type = TYPE_ARG_TYPES(declaration_type); + for (const char *argument_name : std::array{ "count", "parameters" }) + { + tree declaration_tree = build_decl(UNKNOWN_LOCATION, PARM_DECL, + get_identifier(argument_name), TREE_VALUE(parameter_type)); + DECL_CONTEXT(declaration_tree) = fndecl; + DECL_ARG_TYPE(declaration_tree) = TREE_VALUE(parameter_type); + + this->symbols->enter(argument_name, declaration_tree); + DECL_ARGUMENTS(fndecl) = chainon(DECL_ARGUMENTS(fndecl), declaration_tree); + parameter_type = TREE_CHAIN(parameter_type); + } + visit_statements(program->body); + tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(fndecl), + build_int_cst_type(integer_type_node, 0)); + tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); + append_statement(return_stmt); + tree mapping = leave_scope(); + + BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; + DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); + DECL_SAVED_TREE(fndecl) = mapping; + + DECL_EXTERNAL(fndecl) = 0; + DECL_PRESERVE_P(fndecl) = 1; + + pop_cfun(); + gimplify_function_tree(fndecl); + cgraph_node::finalize_function(fndecl, true); + } + + void generic_visitor::visit(boot::unit *unit) + { + for (boot::import_declaration *const declaration : unit->imports) + { + declaration->accept(this); + } + for (boot::constant_declaration *const constant : unit->constants) + { + constant->accept(this); + } + for (boot::variable_declaration *const variable : unit->variables) + { + variable->accept(this); + } + for (boot::procedure_declaration *const procedure : unit->procedures) + { + procedure->accept(this); + } + } + + void generic_visitor::visit(boot::procedure_declaration *definition) + { + tree fndecl = this->symbols->lookup(definition->identifier.name); + + if (!definition->body.has_value()) + { + return; + } + push_struct_function(fndecl, false); + DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc(); + + enter_scope(); + this->bag.enter(this->bag.lookup(definition->identifier.name)->is_procedure()->scope); + + tree argument_chain = DECL_ARGUMENTS(fndecl); + for (; argument_chain != NULL_TREE; argument_chain = TREE_CHAIN(argument_chain)) + { + this->symbols->enter(IDENTIFIER_POINTER(DECL_NAME(argument_chain)), argument_chain); + } + for (boot::constant_declaration *const constant : definition->body.value().constants()) + { + constant->accept(this); + } + for (boot::variable_declaration *const variable : definition->body.value().variables()) + { + variable->accept(this); + } + visit_statements(definition->body.value().body()); + + tree mapping = leave_scope(); + this->bag.leave(); + + BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; + DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); + DECL_SAVED_TREE(fndecl) = mapping; + + DECL_PRESERVE_P(fndecl) = 1; + + pop_cfun(); + gimplify_function_tree(fndecl); + cgraph_node::finalize_function(fndecl, true); + } + + void generic_visitor::enter_scope() + { + this->symbols = std::make_shared(this->symbols); + + // Chain the binding levels. + struct binding_level *new_level = ggc_cleared_alloc(); + new_level->level_chain = f_binding_level; + new_level->statement_list = alloc_stmt_list(); + f_binding_level = new_level; + } + + tree generic_visitor::leave_scope() + { + // Variables are only defined in the top function scope. + tree variables = f_binding_level->level_chain == nullptr ? f_names : NULL_TREE; + tree new_block = build_block(variables, f_binding_level->blocks, NULL_TREE, NULL_TREE); + + for (tree it = f_binding_level->blocks; it != NULL_TREE; it = BLOCK_CHAIN(it)) + { + BLOCK_SUPERCONTEXT(it) = new_block; + } + tree bind_expr = build3(BIND_EXPR, void_type_node, variables, chain_defer(), new_block); + this->symbols = this->symbols->scope(); + + f_binding_level = f_binding_level->level_chain; + + if (f_binding_level != nullptr) + { + f_binding_level->blocks = chainon(f_binding_level->blocks, new_block); + } + return bind_expr; + } + + void generic_visitor::visit(boot::literal *literal) + { + this->current_expression = build_int_cst(elna_int_type_node, literal->value); + } + + void generic_visitor::visit(boot::literal *literal) + { + this->current_expression = build_int_cstu(elna_word_type_node, literal->value); + } + + void generic_visitor::visit(boot::literal *literal) + { + REAL_VALUE_TYPE real_value1; + + mpfr_t number; + mpfr_init2(number, SIGNIFICAND_BITS); + mpfr_set_d(number, literal->value, MPFR_RNDN); + + real_from_mpfr(&real_value1, number, double_type_node, MPFR_RNDN); + + this->current_expression = build_real(double_type_node, real_value1); + + mpfr_clear(number); + } + + void generic_visitor::visit(boot::literal *boolean) + { + this->current_expression = boolean->value ? boolean_true_node : boolean_false_node; + } + + void generic_visitor::visit(boot::literal *character) + { + this->current_expression = build_int_cstu(elna_char_type_node, character->value); + } + + void generic_visitor::visit(boot::literal *) + { + this->current_expression = elna_pointer_nil_node; + } + + void generic_visitor::visit(boot::literal *string) + { + tree index_constant = build_int_cstu(elna_word_type_node, string->value.size()); + tree string_type = build_array_type(elna_char_type_node, build_index_type(index_constant)); + + tree string_literal = build_string(string->value.size(), string->value.c_str()); + TREE_TYPE(string_literal) = string_type; + TREE_CONSTANT(string_literal) = 1; + TREE_READONLY(string_literal) = 1; + TREE_STATIC(string_literal) = 1; + + string_type = TREE_TYPE(elna_string_ptr_field_node); + string_literal = build4(ARRAY_REF, elna_char_type_node, + string_literal, integer_zero_node, NULL_TREE, NULL_TREE); + string_literal = build1(ADDR_EXPR, string_type, string_literal); + + vec *elms = nullptr; + CONSTRUCTOR_APPEND_ELT(elms, elna_string_ptr_field_node, string_literal); + CONSTRUCTOR_APPEND_ELT(elms, elna_string_length_field_node, index_constant); + + this->current_expression = build_constructor(elna_string_type_node, elms); + } + + tree generic_visitor::build_arithmetic_operation(boot::binary_expression *expression, + tree_code operator_code, tree left, tree right) + { + return build_binary_operation(is_numeric_type(TREE_TYPE(left)), + expression, operator_code, left, right, TREE_TYPE(left)); + } + + tree generic_visitor::build_comparison_operation(boot::binary_expression *expression, + tree_code operator_code, tree left, tree right) + { + return build_binary_operation(is_numeric_type(TREE_TYPE(left)) || POINTER_TYPE_P(TREE_TYPE(left)), + expression, operator_code, left, right, elna_bool_type_node); + } + + tree generic_visitor::build_bit_logic_operation(boot::binary_expression *expression, tree left, tree right) + { + location_t expression_location = get_location(&expression->position()); + tree left_type = TREE_TYPE(left); + tree right_type = TREE_TYPE(right); + tree_code logical_code, bit_code; + + if (expression->operation() == boot::binary_operator::conjunction) + { + bit_code = BIT_AND_EXPR; + logical_code = TRUTH_ANDIF_EXPR; + } + else if (expression->operation() == boot::binary_operator::disjunction) + { + bit_code = BIT_IOR_EXPR; + logical_code = TRUTH_ORIF_EXPR; + } + else if (expression->operation() == boot::binary_operator::exclusive_disjunction) + { + bit_code = BIT_XOR_EXPR; + logical_code = TRUTH_XOR_EXPR; + } + else + { + gcc_unreachable(); + } + if (left_type == elna_bool_type_node) + { + return build2_loc(expression_location, logical_code, elna_bool_type_node, left, right); + } + else if (is_integral_type(left_type)) + { + return build2_loc(expression_location, bit_code, left_type, left, right); + } + else + { + error_at(expression_location, "Invalid operands of type '%s' and '%s' for operator %s", + print_type(left_type).c_str(), print_type(right_type).c_str(), + boot::print_binary_operator(expression->operation())); + return error_mark_node; + } + } + + tree generic_visitor::build_equality_operation(boot::binary_expression *expression, tree left, tree right) + { + location_t expression_location = get_location(&expression->position()); + tree_code equality_code, combination_code; + + if (expression->operation() == boot::binary_operator::equals) + { + equality_code = EQ_EXPR; + combination_code = TRUTH_ANDIF_EXPR; + } + else if (expression->operation() == boot::binary_operator::not_equals) + { + equality_code = NE_EXPR; + combination_code = TRUTH_ORIF_EXPR; + } + else + { + gcc_unreachable(); + } + if (TREE_TYPE(left) == elna_string_type_node) + { + tree lhs_length = build3(COMPONENT_REF, TREE_TYPE(elna_string_length_field_node), + left, elna_string_length_field_node, NULL_TREE); + tree lhs_ptr = build3(COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), + left, elna_string_ptr_field_node, NULL_TREE); + + tree rhs_length = build3(COMPONENT_REF, TREE_TYPE(elna_string_length_field_node), + right, elna_string_length_field_node, NULL_TREE); + tree rhs_ptr = build3(COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), + right, elna_string_ptr_field_node, NULL_TREE); + + tree length_equality = build2(equality_code, elna_bool_type_node, lhs_length, rhs_length); + tree memcmp_call = call_built_in(UNKNOWN_LOCATION, "__builtin_memcmp", integer_type_node, + lhs_ptr, rhs_ptr, lhs_length); + tree equals_zero = build2(equality_code, elna_bool_type_node, memcmp_call, integer_zero_node); + + return build2(combination_code, elna_bool_type_node, length_equality, equals_zero); + } + else + { + return build2_loc(expression_location, equality_code, elna_bool_type_node, left, right); + } + } + + void generic_visitor::visit(boot::binary_expression *expression) + { + expression->lhs().accept(this); + tree left = this->current_expression; + tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED); + + expression->rhs().accept(this); + tree right = this->current_expression; + tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED); + + location_t expression_location = get_location(&expression->position()); + + if ((POINTER_TYPE_P(left_type) || POINTER_TYPE_P(right_type)) + && (expression->operation() == boot::binary_operator::sum + || expression->operation() == boot::binary_operator::subtraction)) + { + this->current_expression = do_pointer_arithmetic(expression->operation(), + left, right, expression_location); + if (this->current_expression == error_mark_node) + { + error_at(expression_location, + "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(elna_int_type_node, this->current_expression); + } + return; + } + if (left_type != right_type + && !are_compatible_pointers(left_type, right) + && !are_compatible_pointers(right_type, left) + && !(is_integral_type(left_type) && right_type == elna_word_type_node)) + { + error_at(expression_location, + "invalid operands of type '%s' and '%s' for operator %s", + print_type(left_type).c_str(), print_type(right_type).c_str(), + boot::print_binary_operator(expression->operation())); + this->current_expression = error_mark_node; + return; + } + switch (expression->operation()) + { + case boot::binary_operator::sum: + this->current_expression = build_arithmetic_operation(expression, PLUS_EXPR, left, right); + break; + case boot::binary_operator::subtraction: + this->current_expression = build_arithmetic_operation(expression, MINUS_EXPR, left, right); + break; + case boot::binary_operator::division: + this->current_expression = build_arithmetic_operation(expression, TRUNC_DIV_EXPR, left, right); + break; + case boot::binary_operator::remainder: + this->current_expression = build_arithmetic_operation(expression, TRUNC_MOD_EXPR, left, right); + break; + case boot::binary_operator::multiplication: + this->current_expression = build_arithmetic_operation(expression, MULT_EXPR, left, right); + break; + case boot::binary_operator::less: + this->current_expression = build_comparison_operation(expression, LT_EXPR, left, right); + break; + case boot::binary_operator::greater: + this->current_expression = build_comparison_operation(expression, GT_EXPR, left, right); + break; + case boot::binary_operator::less_equal: + this->current_expression = build_comparison_operation(expression, LE_EXPR, left, right); + break; + case boot::binary_operator::greater_equal: + this->current_expression = build_comparison_operation(expression, GE_EXPR, left, right); + break; + case boot::binary_operator::conjunction: + this->current_expression = build_bit_logic_operation(expression, left, right); + break; + case boot::binary_operator::disjunction: + this->current_expression = build_bit_logic_operation(expression, left, right); + break; + case boot::binary_operator::exclusive_disjunction: + this->current_expression = build_bit_logic_operation(expression, left, right); + break; + case boot::binary_operator::equals: + this->current_expression = build_equality_operation(expression, left, right); + break; + case boot::binary_operator::not_equals: + this->current_expression = build_equality_operation(expression, left, right); + break; + case boot::binary_operator::shift_left: + this->current_expression = build_binary_operation( + is_numeric_type(left_type) && right_type == elna_word_type_node, + expression, LSHIFT_EXPR, left, right, left_type); + break; + case boot::binary_operator::shift_right: + this->current_expression = build_binary_operation( + is_numeric_type(left_type) && right_type == elna_word_type_node, + expression, RSHIFT_EXPR, left, right, left_type); + break; + } + } + + void generic_visitor::visit(boot::unary_expression *expression) + { + expression->operand().accept(this); + location_t location = get_location(&expression->position()); + + switch (expression->operation()) + { + case boot::unary_operator::reference: + this->current_expression = prepare_rvalue(this->current_expression); + TREE_ADDRESSABLE(this->current_expression) = 1; + this->current_expression = build_fold_addr_expr_with_type_loc(location, + this->current_expression, + build_global_pointer_type(TREE_TYPE(this->current_expression))); + TREE_NO_TRAMPOLINE(this->current_expression) = 1; + break; + case boot::unary_operator::negation: + if (TREE_TYPE(this->current_expression) == elna_bool_type_node) + { + this->current_expression = build1_loc(location, TRUTH_NOT_EXPR, + boolean_type_node, this->current_expression); + } + else if (is_integral_type(TREE_TYPE(this->current_expression))) + { + this->current_expression = build1_loc(location, BIT_NOT_EXPR, + TREE_TYPE(this->current_expression), this->current_expression); + } + else + { + error_at(location, "type '%s' cannot be negated", + print_type(TREE_TYPE(this->current_expression)).c_str()); + this->current_expression = error_mark_node; + } + break; + case boot::unary_operator::minus: + if (is_integral_type(TREE_TYPE(this->current_expression))) + { + this->current_expression = fold_build1(NEGATE_EXPR, TREE_TYPE(this->current_expression), + this->current_expression); + } + else + { + error_at(location, "type '%s' cannot be negated", + print_type(TREE_TYPE(this->current_expression)).c_str()); + this->current_expression = error_mark_node; + } + } + } + + void generic_visitor::visit(boot::constant_declaration *definition) + { + location_t definition_location = get_location(&definition->position()); + definition->body().accept(this); + + if (assert_constant(definition_location)) + { + this->current_expression = fold_init(this->current_expression); + } + else + { + this->current_expression = NULL_TREE; + return; + } + tree definition_tree = build_decl(definition_location, CONST_DECL, + get_identifier(definition->identifier.name.c_str()), TREE_TYPE(this->current_expression)); + auto result = this->symbols->enter(definition->identifier.name, definition_tree); + + if (result) + { + DECL_INITIAL(definition_tree) = this->current_expression; + TREE_CONSTANT(definition_tree) = 1; + TREE_READONLY(definition_tree) = 1; + TREE_PUBLIC(definition_tree) = definition->identifier.exported; + + if (!lang_hooks.decls.global_bindings_p()) + { + auto declaration_statement = build1_loc(definition_location, DECL_EXPR, + void_type_node, definition_tree); + append_statement(declaration_statement); + } + } + else + { + error_at(definition_location, "Variable '%s' already declared in this scope", + definition->identifier.name.c_str()); + } + this->current_expression = NULL_TREE; + } + + void generic_visitor::visit(boot::variable_declaration *declaration) + { + for (const auto& variable_identifier : declaration->identifiers) + { + location_t declaration_location = get_location(&declaration->position()); + tree declaration_tree = this->symbols->lookup(variable_identifier.name); + + if (declaration_tree == NULL_TREE) + { + auto variable_symbol = this->bag.lookup(variable_identifier.name)->is_variable(); + + declaration_tree = declare_variable(variable_identifier.name, *variable_symbol, this->symbols); + } + // Set initializer if given. + if (declaration->body != nullptr) + { + declaration->body->accept(this); + if (is_assignable_from(TREE_TYPE(declaration_tree), this->current_expression)) + { + DECL_INITIAL(declaration_tree) = this->current_expression; + } + else + { + error_at(declaration_location, "Cannot initialize variable of type '%s' with a value of type '%s'", + print_type(TREE_TYPE(declaration_tree)).c_str(), + print_type(TREE_TYPE(this->current_expression)).c_str()); + } + } + else if (!declaration->is_extern && POINTER_TYPE_P(TREE_TYPE(declaration_tree))) + { + DECL_INITIAL(declaration_tree) = elna_pointer_nil_node; + } + this->current_expression = NULL_TREE; + + if (lang_hooks.decls.global_bindings_p()) + { + TREE_STATIC(declaration_tree) = !variable_identifier.exported && !declaration->is_extern; + varpool_node::get_create(declaration_tree); + varpool_node::finalize_decl(declaration_tree); + } + else + { + DECL_CONTEXT(declaration_tree) = current_function_decl; + f_names = chainon(f_names, declaration_tree); + + auto declaration_statement = build1_loc(declaration_location, DECL_EXPR, + void_type_node, declaration_tree); + append_statement(declaration_statement); + } + } + } + + void generic_visitor::visit(boot::named_expression *expression) + { + auto symbol = this->symbols->lookup(expression->name); + + if (symbol == NULL_TREE) + { + error_at(get_location(&expression->position()), "Symbol '%s' not declared in the current scope", + expression->name.c_str()); + this->current_expression = error_mark_node; + } + else + { + this->current_expression = symbol; + } + } + + void generic_visitor::visit(boot::array_access_expression *expression) + { + expression->base().accept(this); + tree designator = this->current_expression; + location_t location = get_location(&expression->position()); + + expression->index().accept(this); + if (!is_integral_type(TREE_TYPE(this->current_expression))) + { + error_at(location, "Type '%s' cannot be used as index", + print_type(TREE_TYPE(this->current_expression)).c_str()); + this->current_expression = error_mark_node; + return; + } + tree offset = fold_convert(elna_word_type_node, this->current_expression); + + if (TREE_CODE(TREE_TYPE(designator)) == ARRAY_TYPE) + { + tree element_type = TREE_TYPE(TREE_TYPE(designator)); + + this->current_expression = build4_loc(location, + ARRAY_REF, element_type, designator, offset, size_one_node, NULL_TREE); + } + else if (TREE_TYPE(designator) == elna_string_type_node) + { + offset = build2(MINUS_EXPR, elna_word_type_node, offset, size_one_node); + tree string_ptr = build3_loc(location, COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), + designator, elna_string_ptr_field_node, NULL_TREE); + + tree target_pointer = do_pointer_arithmetic(boot::binary_operator::sum, string_ptr, offset, location); + + this->current_expression = build1_loc(location, INDIRECT_REF, + elna_char_type_node, target_pointer); + } + else + { + error_at(location, "Indexing is not allowed on type '%s'", + print_type(TREE_TYPE(designator)).c_str()); + this->current_expression = error_mark_node; + } + } + + bool generic_visitor::expect_trait_type_only(boot::traits_expression *trait) + { + if (trait->parameters.size() != 1) + { + error_at(get_location(&trait->position()), "Trait '%s' expects 1 argument, got %lu", + trait->name.c_str(), trait->parameters.size()); + this->current_expression = error_mark_node; + return false; + } + this->current_expression = get_inner_alias(trait->types.front(), this->symbols); + + return this->current_expression != error_mark_node; + } + + bool generic_visitor::expect_trait_for_integral_type(boot::traits_expression *trait) + { + if (!expect_trait_type_only(trait)) + { + return false; + } + else if (!is_integral_type(this->current_expression) && TREE_CODE(this->current_expression) != ENUMERAL_TYPE) + { + error_at(get_location(&trait->position()), "Type '%s' does not support trait '%s'", + print_type(this->current_expression).c_str(), trait->name.c_str()); + this->current_expression = error_mark_node; + return false; + } + return true; + } + + void generic_visitor::visit(boot::traits_expression *trait) + { + location_t trait_location = get_location(&trait->position()); + + if (trait->name == "size") + { + if (expect_trait_type_only(trait)) + { + this->current_expression = build1_loc(trait_location, CONVERT_EXPR, elna_word_type_node, + size_in_bytes(this->current_expression)); + } + } + else if (trait->name == "alignment") + { + if (expect_trait_type_only(trait)) + { + this->current_expression = build_int_cstu(elna_word_type_node, + TYPE_ALIGN_UNIT(this->current_expression)); + } + } + else if (trait->name == "min") + { + if (expect_trait_for_integral_type(trait)) + { + this->current_expression = TYPE_MIN_VALUE(this->current_expression); + } + } + else if (trait->name == "max") + { + if (expect_trait_for_integral_type(trait)) + { + this->current_expression = TYPE_MAX_VALUE(this->current_expression); + } + } + else if (trait->name == "offset") + { + if (trait->parameters.size() != 2) + { + error_at(trait_location, "Trait '%s' expects 2 arguments, got %lu", + trait->name.c_str(), trait->parameters.size()); + this->current_expression = error_mark_node; + return; + } + this->current_expression = get_inner_alias(trait->types.front(), this->symbols); + auto field_type = trait->parameters.at(1)->is_named(); + + if (field_type == nullptr) + { + error_at(trait_location, + "The second argument to the offset trait is expected to be a field name," + "got a type expression"); + this->current_expression = error_mark_node; + return; + } + tree field_declaration = find_field_by_name(trait_location, this->current_expression, field_type->name); + + if (field_declaration != error_mark_node) + { + this->current_expression = build1(CONVERT_EXPR, elna_word_type_node, + byte_position(field_declaration)); + } + else + { + this->current_expression = error_mark_node; + } + } + else + { + error_at(get_location(&trait->position()), "Trait '%s' is unknown", trait->name.c_str()); + this->current_expression = error_mark_node; + } + } + + void generic_visitor::visit(boot::field_access_expression *expression) + { + expression->base().accept(this); + location_t expression_location = get_location(&expression->position()); + tree aggregate_type = TREE_TYPE(this->current_expression); + + if (TREE_CODE(aggregate_type) == ARRAY_TYPE && expression->field() == "length") + { + this->current_expression = convert(build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST), + TYPE_MAX_VALUE(TYPE_DOMAIN(aggregate_type))); + } + else if (TREE_CODE(aggregate_type) == ARRAY_TYPE && expression->field() == "ptr") + { + tree ptr_type = build_global_pointer_type(TREE_TYPE(aggregate_type)); + this->current_expression = build1(ADDR_EXPR, + build_qualified_type(ptr_type, TYPE_QUAL_CONST), this->current_expression); + } + else if (TREE_CODE(aggregate_type) == ENUMERAL_TYPE) + { + tree iterator{ NULL_TREE }; + + for (iterator = TYPE_VALUES(aggregate_type); iterator != NULL_TREE; iterator = TREE_CHAIN(iterator)) + { + if (IDENTIFIER_POINTER(TREE_PURPOSE(iterator)) == expression->field()) + { + this->current_expression = TREE_VALUE(iterator); + return; + } + } + this->current_expression = error_mark_node; + error_at(expression_location, "Unknown enumeration member '%s'", expression->field().c_str()); + } + else + { + tree field_declaration = find_field_by_name(expression_location, + TREE_TYPE(this->current_expression), expression->field()); + + if (field_declaration != error_mark_node) + { + this->current_expression = build3_loc(expression_location, COMPONENT_REF, + TREE_TYPE(field_declaration), this->current_expression, + field_declaration, NULL_TREE); + } + } + } + + void generic_visitor::visit(boot::dereference_expression *expression) + { + expression->base().accept(this); + location_t expression_location = get_location(&expression->position()); + tree expression_type = TREE_TYPE(this->current_expression); + + if (POINTER_TYPE_P(expression_type)) + { + this->current_expression = build1_loc(expression_location, INDIRECT_REF, + TREE_TYPE(expression_type), this->current_expression); + } + else + { + error_at(expression_location, "Type '%s' cannot be dereferenced, it is not a pointer", + print_type(expression_type).c_str()); + this->current_expression = error_mark_node; + } + } + + void generic_visitor::visit(boot::assign_statement *statement) + { + statement->lvalue().accept(this); + + tree lvalue = this->current_expression; + location_t statement_location = get_location(&statement->position()); + + statement->rvalue().accept(this); + tree rvalue = prepare_rvalue(this->current_expression); + + if (TREE_CODE(lvalue) == CONST_DECL) + { + error_at(statement_location, "Cannot modify constant '%s'", + statement->lvalue().is_named()->name.c_str()); + } + else if (TYPE_READONLY(TREE_TYPE(lvalue))) + { + error_at(statement_location, "Cannot modify a constant expression of type '%s'", + print_type(TREE_TYPE(lvalue)).c_str()); + } + else if (is_assignable_from(TREE_TYPE(lvalue), rvalue)) + { + tree assignment = build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue, rvalue); + + append_statement(assignment); + } + else + { + error_at(statement_location, "Cannot assign value of type '%s' to variable of type '%s'", + print_type(TREE_TYPE(rvalue)).c_str(), + print_type(TREE_TYPE(lvalue)).c_str()); + } + this->current_expression = NULL_TREE; + } + + void generic_visitor::visit(boot::if_statement *statement) + { + tree endif_label_decl = create_artificial_label(UNKNOWN_LOCATION); + tree goto_endif = build1(GOTO_EXPR, void_type_node, endif_label_decl); + + make_if_branch(statement->body(), goto_endif); + + for (const auto branch : statement->branches) + { + make_if_branch(*branch, goto_endif); + } + if (statement->alternative != nullptr) + { + enter_scope(); + visit_statements(*statement->alternative); + tree mapping = leave_scope(); + append_statement(mapping); + } + tree endif_label_expr = build1(LABEL_EXPR, void_type_node, endif_label_decl); + append_statement(endif_label_expr); + this->current_expression = NULL_TREE; + } + + void generic_visitor::make_if_branch(boot::conditional_statements& branch, tree goto_endif) + { + branch.prerequisite().accept(this); + + if (TREE_TYPE(this->current_expression) != elna_bool_type_node) + { + error_at(get_location(&branch.prerequisite().position()), + "Expected expression of boolean type but its type is %s", + print_type(TREE_TYPE(this->current_expression)).c_str()); + this->current_expression = error_mark_node; + return; + } + tree then_label_decl = build_label_decl("then", UNKNOWN_LOCATION); + tree goto_then = build1(GOTO_EXPR, void_type_node, then_label_decl); + + tree else_label_decl = build_label_decl("else", UNKNOWN_LOCATION); + tree goto_else = build1(GOTO_EXPR, void_type_node, else_label_decl); + + auto cond_expr = build3(COND_EXPR, void_type_node, this->current_expression, goto_then, goto_else); + append_statement(cond_expr); + + tree then_label_expr = build1(LABEL_EXPR, void_type_node, then_label_decl); + append_statement(then_label_expr); + enter_scope(); + + visit_statements(branch.statements); + tree mapping = leave_scope(); + append_statement(mapping); + append_statement(goto_endif); + + tree else_label_expr = build1(LABEL_EXPR, void_type_node, else_label_decl); + append_statement(else_label_expr); + } + + void generic_visitor::visit(boot::import_declaration *) + { + } + + void generic_visitor::visit(boot::while_statement *statement) + { + location_t prerequisite_location = get_location(&statement->body().prerequisite().position()); + tree prerequisite_label_decl = build_label_decl("while_do", prerequisite_location); + auto prerequisite_label_expr = build1_loc(prerequisite_location, LABEL_EXPR, + void_type_node, prerequisite_label_decl); + auto goto_check = build1(GOTO_EXPR, void_type_node, prerequisite_label_decl); + tree branch_end_declaration = build_label_decl("while_end", UNKNOWN_LOCATION); + tree branch_end_expression = build1_loc(UNKNOWN_LOCATION, LABEL_EXPR, void_type_node, branch_end_declaration); + + append_statement(prerequisite_label_expr); + make_if_branch(statement->body(), goto_check); + + for (const auto branch : statement->branches) + { + make_if_branch(*branch, goto_check); + } + append_statement(branch_end_expression); + this->current_expression = NULL_TREE; + } + + void generic_visitor::visit_statements(const std::vector& statements) + { + for (boot::statement *const statement : statements) + { + statement->accept(this); + + if (this->current_expression != NULL_TREE && this->current_expression != error_mark_node) + { + append_statement(this->current_expression); + this->current_expression = NULL_TREE; + } + } + } + + void generic_visitor::visit(boot::return_statement *statement) + { + boot::expression *return_expression = &statement->return_expression(); + location_t statement_position = get_location(&statement->position()); + tree set_result{ NULL_TREE }; + tree return_type = TREE_TYPE(TREE_TYPE(current_function_decl)); + + if (TREE_THIS_VOLATILE(current_function_decl) == 1) + { + error_at(statement_position, "This procedure is not allowed to return"); + return; + } + if (return_expression != nullptr) + { + return_expression->accept(this); + + set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl), + this->current_expression); + } + if (return_type == void_type_node && set_result != NULL_TREE) + { + error_at(statement_position, "Proper procedure is not allowed to return a value"); + } + else if (return_type != void_type_node && set_result == NULL_TREE) + { + error_at(statement_position, "Procedure is expected to return a value of type '%s'", + print_type(return_type).c_str()); + } + else if (return_type != void_type_node && !is_assignable_from(return_type, this->current_expression)) + { + error_at(statement_position, "Cannot return '%s' from a procedure returning '%s'", + print_type(return_type).c_str(), + print_type(TREE_TYPE(this->current_expression)).c_str()); + } + else + { + tree return_stmt = build1_loc(statement_position, RETURN_EXPR, void_type_node, set_result); + append_statement(return_stmt); + } + this->current_expression = NULL_TREE; + } + + void generic_visitor::visit(boot::defer_statement *statement) + { + enter_scope(); + visit_statements(statement->statements); + defer(leave_scope()); + } + + void generic_visitor::visit(boot::case_statement *statement) + { + statement->condition().accept(this); + tree condition_expression = this->current_expression; + tree unqualified_condition = get_qualified_type(TREE_TYPE(this->current_expression), TYPE_UNQUALIFIED); + + if (!INTEGRAL_TYPE_P(unqualified_condition)) + { + error_at(get_location(&statement->condition().position()), + "Case expressions can only be integral numbers, characters and enumerations, given '%s'", + print_type(unqualified_condition).c_str()); + this->current_expression = NULL_TREE; + return; + } + tree end_label_declaration = create_artificial_label(get_location(&statement->position())); + tree switch_statements = alloc_stmt_list(); + + for (const boot::switch_case& case_block : statement->cases) + { + for (boot::expression *const case_label : case_block.labels) + { + case_label->accept(this); + location_t case_location = get_location(&case_label->position()); + + if (assert_constant(case_location) + && !is_assignable_from(unqualified_condition, this->current_expression)) + { + error_at(case_location, "Case type '%s' does not match the expression type '%s'", + print_type(TREE_TYPE(this->current_expression)).c_str(), + print_type(unqualified_condition).c_str()); + this->current_expression = error_mark_node; + } + tree case_label_declaration = create_artificial_label(case_location); + tree case_expression = build_case_label(this->current_expression, NULL_TREE, case_label_declaration); + + append_to_statement_list(case_expression, &switch_statements); + } + enter_scope(); + visit_statements(case_block.statements); + append_to_statement_list(leave_scope(), &switch_statements); + tree goto_end = build1(GOTO_EXPR, void_type_node, end_label_declaration); + + append_to_statement_list(goto_end, &switch_statements); + TREE_USED(end_label_declaration) = 1; + } + if (statement->alternative != nullptr) + { + tree case_label_declaration = create_artificial_label(UNKNOWN_LOCATION); + tree case_expression = build_case_label(NULL_TREE, NULL_TREE, case_label_declaration); + + append_to_statement_list(case_expression, &switch_statements); + + enter_scope(); + visit_statements(*statement->alternative); + append_to_statement_list(leave_scope(), &switch_statements); + + TREE_USED(end_label_declaration) = 1; + } + tree switch_expression = build2(SWITCH_EXPR, TREE_TYPE(condition_expression), + condition_expression, switch_statements); + + append_statement(switch_expression); + + tree end_label_expression = build1(LABEL_EXPR, void_type_node, end_label_declaration); + append_statement(end_label_expression); + + this->current_expression = NULL_TREE; + } + + bool generic_visitor::assert_constant(location_t expression_location) + { + tree constant_expression = extract_constant(this->current_expression); + + if (constant_expression == NULL_TREE) + { + error_at(expression_location, "Expected a constant expression"); + this->current_expression = error_mark_node; + } + else + { + this->current_expression = constant_expression; + } + return this->current_expression != error_mark_node; + } +} diff --git a/gcc/gcc/elna-spec.cc b/gcc/gcc/elna-spec.cc new file mode 100644 index 0000000..5d1ace1 --- /dev/null +++ b/gcc/gcc/elna-spec.cc @@ -0,0 +1,31 @@ +/* Specific flags and argument handling of the Elna front end. + Copyright (C) 2025 Free Software Foundation, Inc. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +void lang_specific_driver(struct cl_decoded_option ** /* in_decoded_options */, + unsigned int * /* in_decoded_options_count */, + int * /*in_added_libraries */) +{ +} + +/* Called before linking. Returns 0 on success and -1 on failure. */ +int lang_specific_pre_link(void) +{ + return 0; +} + +/* Number of extra output files that lang_specific_pre_link may generate. */ +int lang_specific_extra_outfiles = 0; diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc new file mode 100644 index 0000000..de7f6b0 --- /dev/null +++ b/gcc/gcc/elna-tree.cc @@ -0,0 +1,315 @@ +/* Utilities to manipulate GCC trees. + Copyright (C) 2025 Free Software Foundation, Inc. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "elna/gcc/elna-tree.h" +#include "elna/gcc/elna-diagnostic.h" +#include "elna/gcc/elna1.h" + +#include "function.h" +#include "stor-layout.h" +#include "diagnostic-core.h" + +namespace elna::gcc +{ + bool is_integral_type(tree type) + { + gcc_assert(TYPE_P(type)); + return TREE_CODE(type) == INTEGER_TYPE && type != elna_char_type_node; + } + + bool is_numeric_type(tree type) + { + return is_integral_type(type) || type == elna_float_type_node; + } + + bool is_unique_type(tree type) + { + gcc_assert(TYPE_P(type)); + return RECORD_OR_UNION_TYPE_P(type) || TREE_CODE(type) == ENUMERAL_TYPE; + } + + bool is_void_type(tree type) + { + return type == NULL_TREE || type == void_type_node; + } + + bool is_castable_type(tree type) + { + gcc_assert(TYPE_P(type)); + return INTEGRAL_TYPE_P(type) || POINTER_TYPE_P(type) || TREE_CODE(type) == REAL_TYPE; + } + + bool are_compatible_pointers(tree lhs_type, tree rhs) + { + gcc_assert(TYPE_P(lhs_type)); + tree rhs_type = TREE_TYPE(rhs); + + return (POINTER_TYPE_P(lhs_type) && rhs == elna_pointer_nil_node) + || (POINTER_TYPE_P(lhs_type) && lhs_type == rhs_type); + } + + tree prepare_rvalue(tree rvalue) + { + if (DECL_P(rvalue) && TREE_CODE(TREE_TYPE(rvalue)) == FUNCTION_TYPE) + { + return build1(ADDR_EXPR, build_pointer_type_for_mode(TREE_TYPE(rvalue), VOIDmode, true), rvalue); + } + else + { + return rvalue; + } + } + + bool is_assignable_from(tree assignee, tree assignment) + { + return get_qualified_type(TREE_TYPE(assignment), TYPE_UNQUALIFIED) == assignee + || are_compatible_pointers(assignee, assignment); + } + + void append_statement(tree statement_tree) + { + if (!vec_safe_is_empty(f_binding_level->defers)) + { + append_to_statement_list(statement_tree, &f_binding_level->defers->begin()->try_statements); + } + else + { + append_to_statement_list(statement_tree, &f_binding_level->statement_list); + } + } + + void defer(tree statement_tree) + { + defer_scope new_defer{ statement_tree, alloc_stmt_list() }; + vec_safe_insert(f_binding_level->defers, 0, new_defer); + } + + tree chain_defer() + { + if (vec_safe_is_empty(f_binding_level->defers)) + { + return f_binding_level->statement_list; + } + defer_scope *defer_iterator = f_binding_level->defers->begin(); + tree defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, + defer_iterator->try_statements, defer_iterator->defer_block); + int i; + + FOR_EACH_VEC_ELT_FROM(*f_binding_level->defers, i, defer_iterator, 1) + { + append_to_statement_list(defer_tree, &defer_iterator->try_statements); + defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, + defer_iterator->try_statements, defer_iterator->defer_block); + } + return build2(COMPOUND_EXPR, TREE_TYPE(defer_tree), f_binding_level->statement_list, defer_tree); + } + + tree build_field(location_t location, tree record_type, const std::string name, tree type) + { + tree field_declaration = build_decl(location, + FIELD_DECL, get_identifier(name.c_str()), type); + TREE_ADDRESSABLE(field_declaration) = 1; + DECL_CONTEXT(field_declaration) = record_type; + + return field_declaration; + } + + tree do_pointer_arithmetic(boot::binary_operator binary_operator, + tree left, tree right, location_t operation_location) + { + tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED); + tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED); + if (binary_operator == boot::binary_operator::sum) + { + tree pointer{ NULL_TREE }; + tree offset{ NULL_TREE }; + tree pointer_type{ NULL_TREE }; + + if (POINTER_TYPE_P(left_type) && is_integral_type(right_type)) + { + pointer = left; + offset = right; + pointer_type = left_type; + } + else if (is_integral_type(left_type) && POINTER_TYPE_P(right_type)) + { + pointer = right; + offset = left; + pointer_type = right_type; + } + else + { + return error_mark_node; + } + tree size_exp = pointer_type == elna_pointer_type_node + ? size_one_node + : 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_loc(operation_location, POINTER_PLUS_EXPR, TREE_TYPE(pointer), pointer, offset); + } + else if (binary_operator == boot::binary_operator::subtraction) + { + if (POINTER_TYPE_P(left_type) && is_integral_type(right_type)) + { + tree pointer_type = left_type; + tree offset_type = right_type; + 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_loc(operation_location, POINTER_PLUS_EXPR, pointer_type, left, convert_expression); + } + else if (POINTER_TYPE_P(left_type) && POINTER_TYPE_P(right_type) && left_type == right_type) + { + return fold_build2_loc(operation_location, POINTER_DIFF_EXPR, ssizetype, left, right); + } + } + gcc_unreachable(); + } + + tree build_binary_operation(bool condition, boot::binary_expression *expression, + tree_code operator_code, tree left, tree right, tree target_type) + { + location_t expression_location = get_location(&expression->position()); + tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED); + tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED); + + if (condition) + { + return fold_build2_loc(expression_location, operator_code, target_type, left, right); + } + else + { + error_at(expression_location, + "invalid operands of type '%s' and '%s' for operator %s", + print_type(left_type).c_str(), print_type(right_type).c_str(), + boot::print_binary_operator(expression->operation())); + return error_mark_node; + } + } + + tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name) + { + if (type == error_mark_node) + { + return type; + } + tree field_declaration = TYPE_FIELDS(type); + + if (!RECORD_OR_UNION_TYPE_P(type)) + { + error_at(expression_location, "Type '%s' does not have a field named '%s'", + print_type(type).c_str(), field_name.c_str()); + return error_mark_node; + } + while (field_declaration != NULL_TREE) + { + tree declaration_name = DECL_NAME(field_declaration); + const char *identifier_pointer = IDENTIFIER_POINTER(declaration_name); + + if (field_name == identifier_pointer) + { + break; + } + field_declaration = TREE_CHAIN(field_declaration); + } + if (field_declaration == NULL_TREE) + { + error_at(expression_location, "Aggregate type does not have a field '%s'", field_name.c_str()); + return error_mark_node; + } + return field_declaration; + } + + tree build_global_pointer_type(tree type) + { + return build_pointer_type_for_mode(type, VOIDmode, true); + } + + tree build_static_array_type(tree type, const std::uint64_t size) + { + tree upper_bound = build_int_cst_type(integer_type_node, size); + tree range_type = build_range_type(integer_type_node, size_one_node, upper_bound); + + return build_array_type(type, range_type); + } + + tree build_enumeration_type(const std::vector& members) + { + tree composite_type_node = make_node(ENUMERAL_TYPE); + const tree base_type = integer_type_node; + + TREE_TYPE(composite_type_node) = base_type; + ENUM_IS_SCOPED(composite_type_node) = 1; + + tree *pp = &TYPE_VALUES(composite_type_node); + std::size_t order{ 1 }; + + for (const std::string& member : members) + { + tree member_name = get_identifier(member.c_str()); + tree member_declaration = build_decl(UNKNOWN_LOCATION, CONST_DECL, member_name, composite_type_node); + + DECL_CONTEXT(member_declaration) = composite_type_node; + DECL_INITIAL(member_declaration) = build_int_cst_type(composite_type_node, order++); + TREE_CONSTANT(member_declaration) = 1; + TREE_READONLY(member_declaration) = 1; + + TYPE_MAX_VALUE(composite_type_node) = DECL_INITIAL(member_declaration); + + *pp = build_tree_list(member_name, member_declaration); + pp = &TREE_CHAIN(*pp); + } + TYPE_MIN_VALUE(composite_type_node) = DECL_INITIAL(TREE_VALUE(TYPE_VALUES(composite_type_node))); + TYPE_UNSIGNED(composite_type_node) = TYPE_UNSIGNED(base_type); + SET_TYPE_ALIGN(composite_type_node, TYPE_ALIGN(base_type)); + TYPE_SIZE(composite_type_node) = NULL_TREE; + TYPE_PRECISION(composite_type_node) = TYPE_PRECISION(base_type); + + layout_type(composite_type_node); + return composite_type_node; + } + + tree build_label_decl(const char *name, location_t loc) + { + auto label_decl = build_decl(loc, LABEL_DECL, get_identifier(name), void_type_node); + + DECL_CONTEXT(label_decl) = current_function_decl; + + return label_decl; + } + + tree extract_constant(tree expression) + { + int code = TREE_CODE(expression); + + if (code == CONST_DECL) + { + return DECL_INITIAL(expression); + } + else if (TREE_CODE_CLASS(code) == tcc_constant) + { + return expression; + } + return NULL_TREE; + } +} diff --git a/gcc/gcc/elna1.cc b/gcc/gcc/elna1.cc new file mode 100644 index 0000000..0333f70 --- /dev/null +++ b/gcc/gcc/elna1.cc @@ -0,0 +1,256 @@ +/* Language-dependent hooks for Elna. + Copyright (C) 2025 Free Software Foundation, Inc. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "target.h" +#include "function.h" +#include "tree.h" +#include "elna/gcc/elna1.h" +#include "diagnostic.h" +#include "opts.h" +#include "debug.h" +#include "langhooks.h" +#include "langhooks-def.h" + +#include +#include "elna/boot/dependency.h" +#include "elna/gcc/elna-tree.h" +#include "elna/gcc/elna-generic.h" +#include "elna/gcc/elna-diagnostic.h" +#include "elna/gcc/elna-builtins.h" + +tree elna_global_trees[ELNA_TI_MAX]; +hash_map *elna_global_decls = nullptr; + +/* The resulting tree type. */ + +union GTY ((desc("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"), + chain_next("CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), " + "TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN " + "(&%h.generic)) : NULL"))) lang_tree_node +{ + union tree_node GTY ((tag ("0"), desc ("tree_node_structure (&%h)"))) generic; +}; + +/* Language hooks. */ + +static bool elna_langhook_init(void) +{ + build_common_tree_nodes(false); + + elna::gcc::init_ttree(); + elna_global_decls = hash_map::create_ggc(default_hash_map_size); + + build_common_builtin_nodes(); + + return true; +} + +using dependency_state = elna::boot::dependency_state>; + +static elna::boot::dependency elna_parse_file(dependency_state& state, const char *filename) +{ + std::ifstream entry_point{ filename, std::ios::in }; + + if (!entry_point) + { + fatal_error(UNKNOWN_LOCATION, "Cannot open filename %s: %m", filename); + } + elna::gcc::linemap_guard{ filename }; + elna::boot::dependency outcome = elna::boot::read_source(entry_point, filename); + + if (outcome.has_errors()) + { + elna::gcc::report_errors(outcome.errors()); + return outcome; + } + elna::boot::symbol_bag outcome_bag = elna::boot::symbol_bag{ std::move(outcome.unresolved), state.globals }; + + for (const auto& sub_tree : outcome.tree->imports) + { + std::filesystem::path sub_path = "source" / elna::boot::build_path(sub_tree->segments); + std::unordered_map::const_iterator cached_import = + state.cache.find(sub_path); + + if (cached_import == state.cache.end()) + { + elna_parse_file(state, sub_path.c_str()); + cached_import = state.cache.find(sub_path); + } + outcome_bag.add_import(cached_import->second); + } + outcome.errors() = analyze_semantics(filename, outcome.tree, outcome_bag); + + if (outcome.has_errors()) + { + elna::gcc::report_errors(outcome.errors()); + return outcome; + } + state.cache.insert({ filename, outcome_bag }); + elna::gcc::rewrite_symbol_table(outcome_bag.leave(), state.custom); + + return outcome; +} + +static void elna_langhook_parse_file(void) +{ + dependency_state state{ elna::gcc::builtin_symbol_table() }; + + for (unsigned int i = 0; i < num_in_fnames; i++) + { + elna::boot::dependency outcome = elna_parse_file(state, in_fnames[i]); + + linemap_add(line_table, LC_ENTER, 0, in_fnames[i], 1); + elna::gcc::generic_visitor generic_visitor{ state.custom, state.cache.find(in_fnames[i])->second }; + outcome.tree->accept(&generic_visitor); + linemap_add(line_table, LC_LEAVE, 0, NULL, 0); + } +} + +static tree elna_langhook_type_for_mode(enum machine_mode mode, int unsignedp) +{ + if (mode == TYPE_MODE(float_type_node)) + { + return float_type_node; + } + else if (mode == TYPE_MODE(double_type_node)) + { + return double_type_node; + } + if (mode == TYPE_MODE(intQI_type_node)) + { + return unsignedp ? unsigned_intQI_type_node : intQI_type_node; + } + else if (mode == TYPE_MODE(intHI_type_node)) + { + return unsignedp ? unsigned_intHI_type_node : intHI_type_node; + } + else if (mode == TYPE_MODE(intSI_type_node)) + { + return unsignedp ? unsigned_intSI_type_node : intSI_type_node; + } + else if (mode == TYPE_MODE(intDI_type_node)) + { + return unsignedp ? unsigned_intDI_type_node : intDI_type_node; + } + else if (mode == TYPE_MODE(intTI_type_node)) + { + return unsignedp ? unsigned_intTI_type_node : intTI_type_node; + } + else if (mode == TYPE_MODE(integer_type_node)) + { + return unsignedp ? unsigned_type_node : integer_type_node; + } + else if (mode == TYPE_MODE(long_integer_type_node)) + { + return unsignedp ? long_unsigned_type_node : long_integer_type_node; + } + else if (mode == TYPE_MODE(long_long_integer_type_node)) + { + return unsignedp + ? long_long_unsigned_type_node + : long_long_integer_type_node; + } + if (COMPLEX_MODE_P(mode)) + { + if (mode == TYPE_MODE(complex_float_type_node)) + { + return complex_float_type_node; + } + if (mode == TYPE_MODE(complex_double_type_node)) + { + return complex_double_type_node; + } + if (mode == TYPE_MODE(complex_long_double_type_node)) + { + return complex_long_double_type_node; + } + if (mode == TYPE_MODE(complex_integer_type_node) && !unsignedp) + { + return complex_integer_type_node; + } + } + /* gcc_unreachable */ + return nullptr; +} + +static bool global_bindings_p(void) +{ + return current_function_decl == NULL_TREE; +} + +static tree pushdecl(tree decl) +{ + return decl; +} + +static tree elna_langhook_builtin_function(tree decl) +{ + elna_global_decls->put(IDENTIFIER_POINTER(DECL_NAME(decl)), decl); + return decl; +} + +static unsigned int elna_langhook_option_lang_mask(void) +{ + return CL_Elna; +} + +/* Creates an expression whose value is that of EXPR, converted to type TYPE. + This function implements all reasonable scalar conversions. */ +tree convert(tree type, tree expr) +{ + if (error_operand_p(type) || error_operand_p(expr)) + { + return error_mark_node; + } + if (TREE_TYPE(expr) == type) + { + return expr; + } + return error_mark_node; +} + +#undef LANG_HOOKS_NAME +#define LANG_HOOKS_NAME "GNU Elna" + +#undef LANG_HOOKS_INIT +#define LANG_HOOKS_INIT elna_langhook_init + +#undef LANG_HOOKS_PARSE_FILE +#define LANG_HOOKS_PARSE_FILE elna_langhook_parse_file + +#undef LANG_HOOKS_TYPE_FOR_MODE +#define LANG_HOOKS_TYPE_FOR_MODE elna_langhook_type_for_mode + +#undef LANG_HOOKS_GETDECLS +#define LANG_HOOKS_GETDECLS hook_tree_void_null + +#undef LANG_HOOKS_BUILTIN_FUNCTION +#define LANG_HOOKS_BUILTIN_FUNCTION elna_langhook_builtin_function + +#undef LANG_HOOKS_IDENTIFIER_SIZE +#define LANG_HOOKS_IDENTIFIER_SIZE sizeof(struct tree_identifier) + +#undef LANG_HOOKS_OPTION_LANG_MASK +#define LANG_HOOKS_OPTION_LANG_MASK elna_langhook_option_lang_mask + +struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; + +#include "gt-elna-elna1.h" +#include "gtype-elna.h" diff --git a/gcc/gcc/gelna.texi b/gcc/gcc/gelna.texi new file mode 100644 index 0000000..e4bc6ce --- /dev/null +++ b/gcc/gcc/gelna.texi @@ -0,0 +1,135 @@ +\input texinfo @c -*-texinfo-*- +@setfilename gelna.info +@settitle The GNU Elna Compiler + +@c Create a separate index for command line options +@defcodeindex op +@c Merge the standard indexes into a single one. +@syncodeindex fn cp +@syncodeindex vr cp +@syncodeindex ky cp +@syncodeindex pg cp +@syncodeindex tp cp + +@include gcc-common.texi + +@c Copyright years for this manual. +@set copyrights-elna 2025 + +@copying +@c man begin COPYRIGHT +Copyright @copyright{} @value{copyrights-elna} Free Software Foundation, Inc. + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +A copy of the license is included in the +@c man end +section entitled ``GNU Free Documentation License''. +@ignore +@c man begin COPYRIGHT +man page gfdl(7). +@c man end +@end ignore +@end copying + +@ifinfo +@format +@dircategory Software development +@direntry +* Gelna: (gelna). A GCC-based compiler for the Elna language +@end direntry +@end format + +@insertcopying +@end ifinfo + +@titlepage +@title The GNU Elna Compiler +@versionsubtitle +@author Eugen Wissner + +@page +@vskip 0pt plus 1filll +Published by the Free Software Foundation @* +51 Franklin Street, Fifth Floor@* +Boston, MA 02110-1301, USA@* +@sp 1 +@insertcopying +@end titlepage +@contents +@page + +@node Top +@top Introduction + +This manual describes how to use @command{gelna}, the GNU compiler for +the Elna programming language. This manual is specifically about how to +invoke @command{gelna}. + +@menu +* Copying:: The GNU General Public License. +* GNU Free Documentation License:: + How you can share and copy this manual. +* Invoking gelna:: How to run gelna. +* Option Index:: Index of command line options. +* Keyword Index:: Index of concepts. +@end menu + + +@include gpl_v3.texi + +@include fdl.texi + + +@node Invoking gelna +@chapter Invoking gelna + +@c man title gelna A GCC-based compiler for the Elna language + +@ignore +@c man begin SYNOPSIS gelna +gelna [@option{-c}|@option{-S}] + [@option{-g}] [@option{-pg}] + [@option{-o} @var{outfile}] @var{infile}@dots{} + +Only the most useful options are listed here; see below for the +remainder. +@c man end +@c man begin SEEALSO +gpl(7), gfdl(7), fsf-funding(7), gcc(1) +and the Info entries for @file{gelna} and @file{gcc}. +@c man end +@end ignore + +@c man begin DESCRIPTION gelna + +The @command{gelna} command is a frontend to @command{gcc} and +supports many of the same options. @xref{Option Summary, , Option +Summary, gcc, Using the GNU Compiler Collection (GCC)}. This manual +only documents the options specific to @command{gelna}. + +@c man end + +@c man begin OPTIONS gelna + +@c man end + +@node Option Index +@unnumbered Option Index + +@command{gelna}'s command line options are indexed here without any +initial @samp{-} or @samp{--}. Where an option has both positive and +negative forms (such as -foption and -fno-option), relevant entries in +the manual are indexed under the most appropriate form; it may sometimes +be useful to look up both forms. + +@printindex op + +@node Keyword Index +@unnumbered Keyword Index + +@printindex cp + +@bye diff --git a/gcc/gelna.texi b/gcc/gelna.texi deleted file mode 100644 index e4bc6ce..0000000 --- a/gcc/gelna.texi +++ /dev/null @@ -1,135 +0,0 @@ -\input texinfo @c -*-texinfo-*- -@setfilename gelna.info -@settitle The GNU Elna Compiler - -@c Create a separate index for command line options -@defcodeindex op -@c Merge the standard indexes into a single one. -@syncodeindex fn cp -@syncodeindex vr cp -@syncodeindex ky cp -@syncodeindex pg cp -@syncodeindex tp cp - -@include gcc-common.texi - -@c Copyright years for this manual. -@set copyrights-elna 2025 - -@copying -@c man begin COPYRIGHT -Copyright @copyright{} @value{copyrights-elna} Free Software Foundation, Inc. - -Permission is granted to copy, distribute and/or modify this document -under the terms of the GNU Free Documentation License, Version 1.3 or -any later version published by the Free Software Foundation; with no -Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -A copy of the license is included in the -@c man end -section entitled ``GNU Free Documentation License''. -@ignore -@c man begin COPYRIGHT -man page gfdl(7). -@c man end -@end ignore -@end copying - -@ifinfo -@format -@dircategory Software development -@direntry -* Gelna: (gelna). A GCC-based compiler for the Elna language -@end direntry -@end format - -@insertcopying -@end ifinfo - -@titlepage -@title The GNU Elna Compiler -@versionsubtitle -@author Eugen Wissner - -@page -@vskip 0pt plus 1filll -Published by the Free Software Foundation @* -51 Franklin Street, Fifth Floor@* -Boston, MA 02110-1301, USA@* -@sp 1 -@insertcopying -@end titlepage -@contents -@page - -@node Top -@top Introduction - -This manual describes how to use @command{gelna}, the GNU compiler for -the Elna programming language. This manual is specifically about how to -invoke @command{gelna}. - -@menu -* Copying:: The GNU General Public License. -* GNU Free Documentation License:: - How you can share and copy this manual. -* Invoking gelna:: How to run gelna. -* Option Index:: Index of command line options. -* Keyword Index:: Index of concepts. -@end menu - - -@include gpl_v3.texi - -@include fdl.texi - - -@node Invoking gelna -@chapter Invoking gelna - -@c man title gelna A GCC-based compiler for the Elna language - -@ignore -@c man begin SYNOPSIS gelna -gelna [@option{-c}|@option{-S}] - [@option{-g}] [@option{-pg}] - [@option{-o} @var{outfile}] @var{infile}@dots{} - -Only the most useful options are listed here; see below for the -remainder. -@c man end -@c man begin SEEALSO -gpl(7), gfdl(7), fsf-funding(7), gcc(1) -and the Info entries for @file{gelna} and @file{gcc}. -@c man end -@end ignore - -@c man begin DESCRIPTION gelna - -The @command{gelna} command is a frontend to @command{gcc} and -supports many of the same options. @xref{Option Summary, , Option -Summary, gcc, Using the GNU Compiler Collection (GCC)}. This manual -only documents the options specific to @command{gelna}. - -@c man end - -@c man begin OPTIONS gelna - -@c man end - -@node Option Index -@unnumbered Option Index - -@command{gelna}'s command line options are indexed here without any -initial @samp{-} or @samp{--}. Where an option has both positive and -negative forms (such as -foption and -fno-option), relevant entries in -the manual are indexed under the most appropriate form; it may sometimes -be useful to look up both forms. - -@printindex op - -@node Keyword Index -@unnumbered Keyword Index - -@printindex cp - -@bye diff --git a/gcc/testlib/elna-dg.exp b/gcc/testlib/elna-dg.exp new file mode 100644 index 0000000..baefe36 --- /dev/null +++ b/gcc/testlib/elna-dg.exp @@ -0,0 +1,66 @@ +# Copyright (C) 2004-2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +load_lib gcc-dg.exp + +# Define elna callbacks for dg.exp. + +proc elna-dg-test { prog do_what extra_tool_flags } { + set result \ + [gcc-dg-test-1 elna_target_compile $prog $do_what $extra_tool_flags] + + set comp_output [lindex $result 0] + set output_file [lindex $result 1] + + return [list $comp_output $output_file] +} + +proc elna-dg-prune { system text } { + return [gcc-dg-prune $system $text] +} + +# Utility routines. + +# +# Modified dg-runtest that can cycle through a list of optimization options +# as c-torture does. +# + +proc elna-dg-runtest { testcases flags default-extra-flags } { + global runtests + global dg-do-what-default + global tool + + foreach testcase $testcases { + # If we're only testing specific files and this isn't one of them, skip it. + if {![runtest_file_p $runtests $testcase]} { + continue + } + # Check the test directory to detect the test type and set the expectation. + set type [file tail [file dirname $testcase]] + switch $type { + compilable { + set dg-do-what-default "compile" + } + default { + ${tool}_fail $testcase "Unknown test type \"$type\"" + return 0 + } + } + verbose "Testing $type/[file tail $testcase]" + dg-test $testcase $flags ${default-extra-flags} + } +} diff --git a/gcc/testlib/elna.exp b/gcc/testlib/elna.exp new file mode 100644 index 0000000..628a79f --- /dev/null +++ b/gcc/testlib/elna.exp @@ -0,0 +1,155 @@ +# Copyright (C) 2003-2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# +# elna support library routines +# +load_lib prune.exp +load_lib gcc-defs.exp +load_lib timeout.exp +load_lib target-libpath.exp + +# +# ELNA_UNDER_TEST is the compiler under test. +# + + +set gpp_compile_options "" + + +# +# elna_version -- extract and print the version number of the compiler +# + +proc elna_version { } { + global ELNA_UNDER_TEST + + elna_init + + # ignore any arguments after the command + set compiler [lindex $ELNA_UNDER_TEST 0] + + # verify that the compiler exists + if { [is_remote host] || [which $compiler] != 0 } then { + set tmp [remote_exec host "$compiler -v"] + set status [lindex $tmp 0] + set output [lindex $tmp 1] + regexp " version \[^\n\r\]*" $output version + if { $status == 0 && [info exists version] } then { + if [is_remote host] { + clone_output "$compiler $version\n" + } else { + clone_output "[which $compiler] $version\n" + } + } else { + clone_output "Couldn't determine version of [which $compiler]\n" + } + } else { + # compiler does not exist (this should have already been detected) + warning "$compiler does not exist" + } +} + +# +# elna_init -- called at the start of each subdir of tests +# + +proc elna_init { args } { + global subdir + global gpp_initialized + global base_dir + global tmpdir + global libdir + global gluefile wrap_flags + global objdir srcdir + global ALWAYS_ELNAFLAGS + global TOOL_EXECUTABLE TOOL_OPTIONS + global ELNA_UNDER_TEST + global TESTING_IN_BUILD_TREE + global TEST_ALWAYS_FLAGS + + # We set LC_ALL and LANG to C so that we get the same error messages as expected. + setenv LC_ALL C + setenv LANG C + + if ![info exists ELNA_UNDER_TEST] then { + if [info exists TOOL_EXECUTABLE] { + set ELNA_UNDER_TEST $TOOL_EXECUTABLE + } else { + if { [is_remote host] || ! [info exists TESTING_IN_BUILD_TREE] } { + set ELNA_UNDER_TEST [transform gelna] + } else { + set ELNA_UNDER_TEST [findfile $base_dir/../../gelna "$base_dir/../../gelna -B$base_dir/../../" [findfile $base_dir/gelna "$base_dir/gelna -B$base_dir/" [transform gelna]]] + } + } + } + + if ![is_remote host] { + if { [which $ELNA_UNDER_TEST] == 0 } then { + perror "ELNA_UNDER_TEST ($ELNA_UNDER_TEST) does not exist" + exit 1 + } + } + if ![info exists tmpdir] { + set tmpdir "/tmp" + } + + if [info exists gluefile] { + unset gluefile + } + + elna_maybe_build_wrapper "${tmpdir}/elna-testglue.o" + + set ALWAYS_ELNAFLAGS "" + + # TEST_ALWAYS_FLAGS are flags that should be passed to every + # compilation. They are passed first to allow individual + # tests to override them. + if [info exists TEST_ALWAYS_FLAGS] { + lappend ALWAYS_ELNAFLAGS "additional_flags=$TEST_ALWAYS_FLAGS" + } + + if [info exists TOOL_OPTIONS] { + lappend ALWAYS_ELNAFLAGS "additional_flags=$TOOL_OPTIONS" + } + + verbose -log "ALWAYS_ELNAFLAGS set to $ALWAYS_ELNAFLAGS" + + verbose "elna is initialized" 3 +} + +# +# elna_target_compile -- compile a source file +# + +proc elna_target_compile { source dest type options } { + global tmpdir + global gluefile wrap_flags + global ALWAYS_ELNAFLAGS + global ELNA_UNDER_TEST + + if { [target_info needs_status_wrapper] != "" && [info exists gluefile] } { + lappend options "libs=${gluefile}" + lappend options "ldflags=${wrap_flags}" + } + + lappend options "timeout=[timeout_value]" + lappend options "compiler=$ELNA_UNDER_TEST" + + set options [concat "$ALWAYS_ELNAFLAGS" $options] + set options [dg-additional-files-options $options $source $dest $type] + return [target_compile $source $dest $type $options] +} diff --git a/rakelib/gcc.rake b/rakelib/gcc.rake index 3f36ce9..7e00d1b 100644 --- a/rakelib/gcc.rake +++ b/rakelib/gcc.rake @@ -58,32 +58,57 @@ def download_and_pipe(url, target, command) end end +def link_frontend(source, destination) + File.symlink Pathname.new(source).relative_path_from(destination), (destination + File.basename(source)) +end + namespace :gcc do # Dependencies. GCC_VERSION = "15.3.0" HOST_GCC = 'build/host/gcc' - HOST_INSTALL = 'build/host/install' + GCC_TREE = Pathname.new "build/tools/gcc-#{GCC_VERSION}" GCC_PATCH = 'https://raw.githubusercontent.com/Homebrew/homebrew-core/refs/heads/main/Patches/gcc/gcc-15.3.0.diff' directory HOST_GCC - directory HOST_INSTALL + directory 'build/host/install' directory 'build/tools' - desc 'Download and configure the bootstrap compiler' - task configure: ['build/tools', HOST_GCC, HOST_INSTALL] do + desc 'Download the bootstrap compiler and its prerequisites' + task download: 'build/tools' do url = URI.parse "https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz" - build_target = find_build_target - source_directory = Pathname.new "build/tools/gcc-#{GCC_VERSION}" - frontend_link = source_directory + 'gcc' - download_and_pipe url, source_directory.dirname, ['tar', '-Jxv'] - download_and_pipe URI.parse(GCC_PATCH), source_directory, ['patch', '-p1'] + download_and_pipe url, GCC_TREE.dirname, ['tar', '-Jxv'] + download_and_pipe URI.parse(GCC_PATCH), GCC_TREE, ['patch', '-p1'] + + sh 'contrib/download_prerequisites', chdir: GCC_TREE.to_path + end + + desc 'Link the frontend into the GCC source tree' + task :link do + source_destination = GCC_TREE + 'gcc/elna' + test_destination = GCC_TREE + 'gcc/testsuite/elna.dg' - sh 'contrib/download_prerequisites', chdir: source_directory.to_path - File.symlink Pathname.new('.').relative_path_from(frontend_link), (frontend_link + 'elna') + rm_rf [source_destination, test_destination] + mkdir_p [source_destination, test_destination] + + FileList['boot', 'include', 'COPYING3', 'README.md', 'gcc/gcc', 'gcc/*.in', 'gcc/lang*'].each do |file| + link_frontend file, source_destination + end + FileList['testsuite/*', 'gcc/dg.exp', 'README.md'].each do |file| + link_frontend file, test_destination + end + destination = GCC_TREE + 'gcc/testsuite/lib' + FileList['gcc/testlib/*'].each do |file| + rm_f (destination + File.basename(file)) + link_frontend file, destination + end + end + desc 'Configure the bootstrap compiler' + task configure: [HOST_GCC, 'build/host/install'] do |t| + build_target = find_build_target configure_options = [ - "--prefix=#{File.realpath HOST_INSTALL}", + "--prefix=#{File.realpath t.prerequisites.last}", '--enable-languages=c,c++,jit,elna', '--disable-bootstrap', '--disable-multilib', @@ -93,13 +118,13 @@ namespace :gcc do "--build=#{build_target}", "--host=#{build_target}" ] - if File.symlink? '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' - configure_options << '--with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' - end + mac_os_sdk = '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' + configure_options << "--with-sysroot=#{mac_os_sdk}" if File.symlink? mac_os_sdk + env = ENV.slice 'CC', 'CXX' env['CFLAGS'] = env['CXXFLAGS'] = '-O0 -g -fPIC -I/opt/homebrew/opt/flex/include' - configure = source_directory.relative_path_from(HOST_GCC) + 'configure' + configure = GCC_TREE.relative_path_from(HOST_GCC) + 'configure' sh env, configure.to_path, *configure_options, chdir: HOST_GCC end @@ -108,7 +133,12 @@ namespace :gcc do sh 'make', '-j', Etc.nprocessors.to_s, chdir: HOST_GCC sh 'make', 'install', chdir: HOST_GCC end + + desc 'Run tests' + task :check do + sh 'make', 'check-elna', chdir: File.join(HOST_GCC, 'gcc') + end end desc 'Build the bootstrap compiler' -task gcc: %w[gcc:configure gcc:make] +task gcc: %w[gcc:download gcc:link gcc:configure gcc:make] diff --git a/testsuite/compilable/empty_proc.elna b/testsuite/compilable/empty_proc.elna new file mode 100644 index 0000000..fb07285 --- /dev/null +++ b/testsuite/compilable/empty_proc.elna @@ -0,0 +1,4 @@ +proc f() +end + +end. -- cgit v1.2.3