summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2025-12-02 10:22:06 +0100
committerEugen Wissner <belka@caraus.de>2025-12-02 17:14:18 +0100
commit23b6f074c7f560d701e9a1fa5713a965af3a18a3 (patch)
tree87549a4eba3da8d8ed6e3fbb2e337e152a8bc96a /gcc
parent5f7d83974114c73327ce9fff3635927df050b5e4 (diff)
downloadelna-23b6f074c7f560d701e9a1fa5713a965af3a18a3.tar.gz
Merge GCC frontend into the branch
Diffstat (limited to 'gcc')
-rw-r--r--gcc/Make-lang.in174
-rw-r--r--gcc/README.md42
-rw-r--r--gcc/config-lang.in37
-rw-r--r--gcc/elna-builtins.cc274
-rw-r--r--gcc/elna-diagnostic.cc167
-rw-r--r--gcc/elna-generic.cc1277
-rw-r--r--gcc/elna-spec.cc31
-rw-r--r--gcc/elna-tree.cc315
-rw-r--r--gcc/elna1.cc256
-rw-r--r--gcc/gelna.texi135
-rw-r--r--gcc/lang-specs.h28
-rw-r--r--gcc/lang.opt23
-rw-r--r--gcc/lang.opt.urls2
13 files changed, 2761 insertions, 0 deletions
diff --git a/gcc/Make-lang.in b/gcc/Make-lang.in
new file mode 100644
index 0000000..e25fc6d
--- /dev/null
+++ b/gcc/Make-lang.in
@@ -0,0 +1,174 @@
+# Top level -*- makefile -*- fragment for the 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
+# <http://www.gnu.org/licenses/>.
+
+ELNA_INSTALL_NAME := $(shell echo gelna|sed '$(program_transform_name)')
+ELNA_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gelna|sed '$(program_transform_name)')
+
+elna: elna1$(exeext)
+
+.PHONY: elna
+
+# Driver
+
+ELNA_OBJS = \
+ $(GCC_OBJS) \
+ elna/elna-spec.o \
+ $(END)
+
+gelna$(exeext): $(ELNA_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS)
+ +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
+ $(ELNA_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a \
+ $(EXTRA_GCC_LIBS) $(LIBS)
+
+# Create a version of the gelna driver which calls the cross-compiler.
+gelna-cross$(exeext): gelna$(exeext)
+ -rm -f gelna-cross$(exeext)
+ cp gelna$(exeext) gelna-cross$(exeext)
+
+# The compiler proper
+
+elna_OBJS = \
+ elna/elna1.o \
+ elna/elna-generic.o \
+ elna/elna-diagnostic.o \
+ elna/elna-tree.o \
+ elna/elna-builtins.o \
+ elna/ast.o \
+ elna/dependency.o \
+ elna/driver.o \
+ elna/lexer.o \
+ elna/parser.o \
+ elna/semantic.o \
+ elna/symbol.o \
+ elna/result.o \
+ $(END)
+
+elna1$(exeext): attribs.o $(elna_OBJS) $(BACKEND) $(LIBDEPS)
+ +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
+ attribs.o $(elna_OBJS) $(BACKEND) $(LIBS) $(BACKENDLIBS)
+
+elna.all.cross: gelna-cross$(exeext)
+
+elna.start.encap: gelna$(exeext)
+elna.rest.encap:
+
+# No elna-specific selftests.
+selftest-elna:
+
+ELNA_TEXI_FILES = \
+ elna/gcc/gelna.texi \
+ $(srcdir)/doc/include/fdl.texi \
+ $(srcdir)/doc/include/gpl_v3.texi \
+ $(srcdir)/doc/include/funding.texi \
+ $(srcdir)/doc/include/gcc-common.texi \
+ gcc-vers.texi
+
+elna.install-common: installdirs
+ -rm -f $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext)
+ $(INSTALL_PROGRAM) gelna$(exeext) $(DESTDIR)$(bindir)/$(ELNA_INSTALL_NAME)$(exeext)
+ -if test -f elna1$(exeext); then \
+ if test -f gelna-cross$(exeext); then \
+ :; \
+ else \
+ rm -f $(DESTDIR)$(bindir)/$(ELNA_TARGET_INSTALL_NAME)$(exeext); \
+ ( cd $(DESTDIR)$(bindir) && \
+ $(LN) $(ELNA_INSTALL_NAME)$(exeext) $(ELNA_TARGET_INSTALL_NAME)$(exeext) ); \
+ fi; \
+ fi
+
+$(build_htmldir)/gelna/index.html: $(ELNA_TEXI_FILES)
+ $(mkinstalldirs) $(@D)
+ rm -f $(@D)/*
+ $(TEXI2HTML) -I $(gcc_docdir)/include -I $(srcdir)/elna -o $(@D) $<
+
+# Required goals, they still do nothing
+elna.install-man:
+elna.install-info:
+elna.install-pdf:
+elna.install-plugin:
+
+elna.install-html: $(build_htmldir)/gelna
+ @$(NORMAL_INSTALL)
+ test -z "$(htmldir)" || $(mkinstalldirs) "$(DESTDIR)$(htmldir)"
+ @for p in $(build_htmldir)/gelna; do \
+ if test -f "$$p" || test -d "$$p"; then d=""; else d="$(srcdir)/"; fi; \
+ f=$(html__strip_dir) \
+ if test -d "$$d$$p"; then \
+ echo " $(mkinstalldirs) '$(DESTDIR)$(htmldir)/$$f'"; \
+ $(mkinstalldirs) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \
+ echo " $(INSTALL_DATA) '$$d$$p'/* '$(DESTDIR)$(htmldir)/$$f'"; \
+ $(INSTALL_DATA) "$$d$$p"/* "$(DESTDIR)$(htmldir)/$$f"; \
+ else \
+ echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(htmldir)/$$f'"; \
+ $(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(htmldir)/$$f"; \
+ fi; \
+ done
+
+elna.info:
+elna.dvi:
+elna.pdf:
+elna.html: $(build_htmldir)/gelna/index.html
+elna.man:
+elna.mostlyclean:
+elna.clean:
+elna.distclean:
+elna.maintainer-clean:
+
+# make uninstall
+elna.uninstall:
+ -rm -f gelna$(exeext) elna1$(exeext)
+ -rm -f $(elna_OBJS)
+
+# Used for handling bootstrap
+elna.stage1: stage1-start
+ -mv elna/*$(objext) stage1/elna
+elna.stage2: stage2-start
+ -mv elna/*$(objext) stage2/elna
+elna.stage3: stage3-start
+ -mv elna/*$(objext) stage3/elna
+elna.stage4: stage4-start
+ -mv elna/*$(objext) stage4/elna
+elna.stageprofile: stageprofile-start
+ -mv elna/*$(objext) stageprofile/elna
+elna.stagefeedback: stagefeedback-start
+ -mv elna/*$(objext) stagefeedback/elna
+
+ELNA_INCLUDES = -I $(srcdir)/elna/include -I elna/generated
+ELNA_CXXFLAGS = -std=c++17
+
+elna/%.o: elna/frontend/%.cc elna/generated/parser.hh elna/generated/location.hh
+ $(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $<
+ $(POSTCOMPILE)
+
+elna/%.o: elna/generated/%.cc elna/generated/parser.hh elna/generated/location.hh
+ $(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $<
+ $(POSTCOMPILE)
+
+elna/%.o: elna/gcc/%.cc elna/generated/parser.hh elna/generated/location.hh
+ $(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $<
+ $(POSTCOMPILE)
+
+elna/generated/parser.cc: elna/frontend/parser.yy
+ mkdir -p $(dir $@)
+ $(BISON) -d -o $@ $<
+
+elna/generated/parser.hh elna/generated/location.hh: elna/generated/parser.cc
+ @touch $@
+
+elna/generated/lexer.cc: elna/frontend/lexer.ll
+ mkdir -p $(dir $@)
+ $(FLEX) -o $@ $<
diff --git a/gcc/README.md b/gcc/README.md
new file mode 100644
index 0000000..99d03c3
--- /dev/null
+++ b/gcc/README.md
@@ -0,0 +1,42 @@
+# Elna programming language
+
+## Current implementation
+
+This repository contains a GCC frontend for Elna. After finishing the frontend
+I'm planning to rewrite the compiler in Elna itself with its own backend and
+a hand-written parser. So GCC gives a way to have a simple bootstrap compiler
+and a possbility to compile Elna programs for different platforms.
+
+## Grammar
+
+Flex and bison grammar specifications, `lexer.ll` and `parser.yy`, can be found
+in the `boot/` directory.
+
+## Build
+
+The frontend requires GCC 15.2.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
+and Mac OS. In the latter case GCC is patched with the patches used by Homebrew
+(official GCC doesn't support Apple silicon targets). Invoke with
+
+```sh
+rake boot
+```
+
+`gcc` binary is used by default, but a different gcc version can be specified
+by passing `CC` and `CXX` environment variables to rake, e.g.:
+
+```sh
+rake CC=gcc-15 CXX=g++-15 boot
+```
+
+See `rake -T` for more tasks. The GCC source is under `build/tools`. The
+installation path is `build/host/install`.
diff --git a/gcc/config-lang.in b/gcc/config-lang.in
new file mode 100644
index 0000000..0cbbe1f
--- /dev/null
+++ b/gcc/config-lang.in
@@ -0,0 +1,37 @@
+# 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
+# <http://www.gnu.org/licenses/>.
+
+# Configure looks for the existence of this file to auto-config each language.
+# We define several parameters used by configure:
+#
+# language - name of language as it would appear in $(LANGUAGES)
+# boot_language - "yes" if we need to build this language in stage1
+# compilers - value to add to $(COMPILERS)
+
+language="elna"
+gcc_subdir="elna/gcc"
+
+compilers="elna1\$(exeext)"
+
+target_libs=""
+
+gtfiles="\$(srcdir)/elna/gcc/elna1.cc \$(srcdir)/elna/include/elna/gcc/elna1.h"
+
+lang_requires_boot_languages=c++
+
+# Do not build by default
+build_by_default="no"
diff --git a/gcc/elna-builtins.cc b/gcc/elna-builtins.cc
new file mode 100644
index 0000000..cf06df8
--- /dev/null
+++ b/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
+<http://www.gnu.org/licenses/>. */
+
+#include <algorithm>
+
+#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> 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<symbol_table> builtin_symbol_table()
+ {
+ std::shared_ptr<elna::gcc::symbol_table> symbol_table = std::make_shared<elna::gcc::symbol_table>();
+
+ declare_builtin_type(symbol_table, "Int", elna_int_type_node);
+ declare_builtin_type(symbol_table, "Word", elna_word_type_node);
+ declare_builtin_type(symbol_table, "Char", elna_char_type_node);
+ declare_builtin_type(symbol_table, "Bool", elna_bool_type_node);
+ declare_builtin_type(symbol_table, "Pointer", elna_pointer_type_node);
+ declare_builtin_type(symbol_table, "Float", elna_float_type_node);
+
+ tree string_declaration = declare_builtin_type(symbol_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 symbol_table;
+ }
+
+ tree build_composite_type(const std::vector<frontend::type_field>& fields, tree composite_type_node,
+ std::shared_ptr<symbol_table> 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 frontend::procedure_type& procedure, std::shared_ptr<symbol_table> symbols)
+ {
+ std::vector<tree> 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 frontend::type& type, std::shared_ptr<symbol_table> symbols)
+ {
+ if (auto reference = type.get<frontend::primitive_type>())
+ {
+ auto looked_up = symbols->lookup(reference->identifier);
+ gcc_assert(looked_up != NULL_TREE);
+
+ return TREE_TYPE(looked_up);
+ }
+ else if (auto reference = type.get<frontend::record_type>())
+ {
+ 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<frontend::union_type>())
+ {
+ 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<frontend::enumeration_type>())
+ {
+ return build_enumeration_type(reference->members);
+ }
+ else if (auto reference = type.get<frontend::pointer_type>())
+ {
+ return build_global_pointer_type(get_inner_alias(reference->base, symbols));
+ }
+ else if (auto reference = type.get<frontend::array_type>())
+ {
+ tree base = get_inner_alias(reference->base, symbols);
+
+ return build_static_array_type(base, reference->size);
+ }
+ else if (auto reference = type.get<frontend::procedure_type>())
+ {
+ auto procedure = build_procedure_type(*reference, symbols);
+
+ return build_global_pointer_type(procedure);
+ }
+ else if (auto reference = type.get<frontend::alias_type>())
+ {
+ return TREE_TYPE(handle_symbol(reference->name, reference, symbols));
+ }
+ return error_mark_node;
+ }
+
+ tree handle_symbol(const std::string& symbol_name, std::shared_ptr<frontend::alias_type> reference,
+ std::shared_ptr<symbol_table> 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 frontend::procedure_info& info,
+ std::shared_ptr<symbol_table> 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(&parameter_type, declaration_type);
+
+ std::vector<std::string>::const_iterator parameter_name = info.names.cbegin();
+
+ for (frontend::type parameter : info.symbol.parameters)
+ {
+ tree declaration_tree = build_decl(UNKNOWN_LOCATION, PARM_DECL,
+ get_identifier(parameter_name->c_str()), function_args_iter_cond(&parameter_type));
+ DECL_CONTEXT(declaration_tree) = fndecl;
+ DECL_ARG_TYPE(declaration_tree) = function_args_iter_cond(&parameter_type);
+
+ argument_chain = chainon(argument_chain, declaration_tree);
+ function_args_iter_next(&parameter_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 frontend::variable_info& info,
+ std::shared_ptr<symbol_table> 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 frontend::type_info& info, std::shared_ptr<symbol_table> symbols)
+ {
+ // The top level symbol table has basic (builtin) types in it which are not aliases.
+ if (auto alias_type = info.symbol.get<frontend::alias_type>())
+ {
+ tree type_declaration = handle_symbol(name, alias_type, symbols);
+
+ TREE_PUBLIC(type_declaration) = info.exported;
+ }
+ }
+
+ void rewrite_symbol_table(std::shared_ptr<frontend::symbol_table> info_table, std::shared_ptr<symbol_table> 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
new file mode 100644
index 0000000..162d6cb
--- /dev/null
+++ b/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
+<http://www.gnu.org/licenses/>. */
+
+#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 frontend::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 "<<unknown-type>>";
+ }
+ gcc_unreachable();
+ }
+
+ void report_errors(const std::deque<std::unique_ptr<frontend::error>>& errors)
+ {
+ for (const auto& error : errors)
+ {
+ location_t gcc_location{ UNKNOWN_LOCATION };
+
+ if (error->position.line != 0 || error->position.column != 0)
+ {
+ gcc_location = elna::gcc::get_location(&error->position);
+ }
+ error_at(gcc_location, error->what().c_str());
+ }
+ }
+}
diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc
new file mode 100644
index 0000000..b37b111
--- /dev/null
+++ b/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
+<http://www.gnu.org/licenses/>. */
+
+#include <array>
+
+#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> symbol_table, elna::frontend::symbol_bag bag)
+ : bag(bag), symbols(symbol_table)
+ {
+ }
+
+ void generic_visitor::build_procedure_call(location_t call_location,
+ tree procedure_address, const std::vector<frontend::expression *>& arguments)
+ {
+ vec<tree, va_gc> *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 (frontend::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<frontend::expression *>& arguments)
+ {
+ vec<constructor_elt, va_gc> *tree_arguments = nullptr;
+ tree record_fields = TYPE_FIELDS(symbol);
+ for (frontend::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<frontend::expression *>& 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(frontend::procedure_call *call)
+ {
+ location_t call_location = get_location(&call->position());
+
+ if (frontend::variable_expression *named_call = call->callable().is_variable())
+ {
+ if (named_call->name == "assert")
+ {
+ build_assert_builtin(call_location, call->arguments);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void generic_visitor::visit(frontend::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(frontend::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(frontend::program *program)
+ {
+ visit(static_cast<frontend::unit *>(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<language_function>();
+
+ enter_scope();
+
+ tree parameter_type = TYPE_ARG_TYPES(declaration_type);
+ for (const char *argument_name : std::array<const char *, 2>{ "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(frontend::unit *unit)
+ {
+ for (frontend::import_declaration *const declaration : unit->imports)
+ {
+ declaration->accept(this);
+ }
+ for (frontend::constant_declaration *const constant : unit->constants)
+ {
+ constant->accept(this);
+ }
+ for (frontend::variable_declaration *const variable : unit->variables)
+ {
+ variable->accept(this);
+ }
+ for (frontend::procedure_declaration *const procedure : unit->procedures)
+ {
+ procedure->accept(this);
+ }
+ }
+
+ void generic_visitor::visit(frontend::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<language_function>();
+
+ 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 (frontend::constant_declaration *const constant : definition->body.value().constants())
+ {
+ constant->accept(this);
+ }
+ for (frontend::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<symbol_table>(this->symbols);
+
+ // Chain the binding levels.
+ struct binding_level *new_level = ggc_cleared_alloc<binding_level>();
+ 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(frontend::literal<std::int32_t> *literal)
+ {
+ this->current_expression = build_int_cst(elna_int_type_node, literal->value);
+ }
+
+ void generic_visitor::visit(frontend::literal<std::uint32_t> *literal)
+ {
+ this->current_expression = build_int_cstu(elna_word_type_node, literal->value);
+ }
+
+ void generic_visitor::visit(frontend::literal<double> *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(frontend::literal<bool> *boolean)
+ {
+ this->current_expression = boolean->value ? boolean_true_node : boolean_false_node;
+ }
+
+ void generic_visitor::visit(frontend::literal<unsigned char> *character)
+ {
+ this->current_expression = build_int_cstu(elna_char_type_node, character->value);
+ }
+
+ void generic_visitor::visit(frontend::literal<nullptr_t> *)
+ {
+ this->current_expression = elna_pointer_nil_node;
+ }
+
+ void generic_visitor::visit(frontend::literal<std::string> *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<constructor_elt, va_gc> *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(frontend::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(frontend::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(frontend::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() == frontend::binary_operator::conjunction)
+ {
+ bit_code = BIT_AND_EXPR;
+ logical_code = TRUTH_ANDIF_EXPR;
+ }
+ else if (expression->operation() == frontend::binary_operator::disjunction)
+ {
+ bit_code = BIT_IOR_EXPR;
+ logical_code = TRUTH_ORIF_EXPR;
+ }
+ else if (expression->operation() == frontend::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(),
+ elna::frontend::print_binary_operator(expression->operation()));
+ return error_mark_node;
+ }
+ }
+
+ tree generic_visitor::build_equality_operation(frontend::binary_expression *expression, tree left, tree right)
+ {
+ location_t expression_location = get_location(&expression->position());
+ tree_code equality_code, combination_code;
+
+ if (expression->operation() == frontend::binary_operator::equals)
+ {
+ equality_code = EQ_EXPR;
+ combination_code = TRUTH_ANDIF_EXPR;
+ }
+ else if (expression->operation() == frontend::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(frontend::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() == frontend::binary_operator::sum
+ || expression->operation() == frontend::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",
+ frontend::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(),
+ frontend::print_binary_operator(expression->operation()));
+ this->current_expression = error_mark_node;
+ return;
+ }
+ switch (expression->operation())
+ {
+ case frontend::binary_operator::sum:
+ this->current_expression = build_arithmetic_operation(expression, PLUS_EXPR, left, right);
+ break;
+ case frontend::binary_operator::subtraction:
+ this->current_expression = build_arithmetic_operation(expression, MINUS_EXPR, left, right);
+ break;
+ case frontend::binary_operator::division:
+ this->current_expression = build_arithmetic_operation(expression, TRUNC_DIV_EXPR, left, right);
+ break;
+ case frontend::binary_operator::remainder:
+ this->current_expression = build_arithmetic_operation(expression, TRUNC_MOD_EXPR, left, right);
+ break;
+ case frontend::binary_operator::multiplication:
+ this->current_expression = build_arithmetic_operation(expression, MULT_EXPR, left, right);
+ break;
+ case frontend::binary_operator::less:
+ this->current_expression = build_comparison_operation(expression, LT_EXPR, left, right);
+ break;
+ case frontend::binary_operator::greater:
+ this->current_expression = build_comparison_operation(expression, GT_EXPR, left, right);
+ break;
+ case frontend::binary_operator::less_equal:
+ this->current_expression = build_comparison_operation(expression, LE_EXPR, left, right);
+ break;
+ case frontend::binary_operator::greater_equal:
+ this->current_expression = build_comparison_operation(expression, GE_EXPR, left, right);
+ break;
+ case frontend::binary_operator::conjunction:
+ this->current_expression = build_bit_logic_operation(expression, left, right);
+ break;
+ case frontend::binary_operator::disjunction:
+ this->current_expression = build_bit_logic_operation(expression, left, right);
+ break;
+ case frontend::binary_operator::exclusive_disjunction:
+ this->current_expression = build_bit_logic_operation(expression, left, right);
+ break;
+ case frontend::binary_operator::equals:
+ this->current_expression = build_equality_operation(expression, left, right);
+ break;
+ case frontend::binary_operator::not_equals:
+ this->current_expression = build_equality_operation(expression, left, right);
+ break;
+ case frontend::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 frontend::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(frontend::unary_expression *expression)
+ {
+ expression->operand().accept(this);
+ location_t location = get_location(&expression->position());
+
+ switch (expression->operation())
+ {
+ case frontend::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 frontend::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 frontend::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(frontend::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(frontend::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(frontend::variable_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(frontend::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(frontend::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(frontend::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(frontend::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(frontend::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(frontend::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(frontend::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(frontend::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_variable()->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(frontend::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(frontend::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(frontend::import_declaration *)
+ {
+ }
+
+ void generic_visitor::visit(frontend::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<frontend::statement *>& statements)
+ {
+ for (frontend::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(frontend::return_statement *statement)
+ {
+ frontend::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(frontend::defer_statement *statement)
+ {
+ enter_scope();
+ visit_statements(statement->statements);
+ defer(leave_scope());
+ }
+
+ void generic_visitor::visit(frontend::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 frontend::switch_case& case_block : statement->cases)
+ {
+ for (frontend::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
new file mode 100644
index 0000000..5d1ace1
--- /dev/null
+++ b/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
+<http://www.gnu.org/licenses/>. */
+
+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
new file mode 100644
index 0000000..93f796b
--- /dev/null
+++ b/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
+<http://www.gnu.org/licenses/>. */
+
+#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(frontend::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 == frontend::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 == frontend::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, frontend::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(),
+ elna::frontend::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<std::string>& 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
new file mode 100644
index 0000000..448a24c
--- /dev/null
+++ b/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
+<http://www.gnu.org/licenses/>. */
+
+#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 <fstream>
+#include "elna/frontend/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<nofree_string_hash, tree> *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<nofree_string_hash, tree>::create_ggc(default_hash_map_size);
+
+ build_common_builtin_nodes();
+
+ return true;
+}
+
+using dependency_state = elna::frontend::dependency_state<std::shared_ptr<elna::gcc::symbol_table>>;
+
+static elna::frontend::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::frontend::dependency outcome = elna::frontend::read_source(entry_point, filename);
+
+ if (outcome.has_errors())
+ {
+ elna::gcc::report_errors(outcome.errors());
+ return outcome;
+ }
+ elna::frontend::symbol_bag outcome_bag = elna::frontend::symbol_bag{ std::move(outcome.unresolved), state.globals };
+
+ for (const auto& sub_tree : outcome.tree->imports)
+ {
+ std::filesystem::path sub_path = "source" / elna::frontend::build_path(sub_tree->segments);
+ std::unordered_map<std::filesystem::path, elna::frontend::symbol_bag>::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::frontend::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/gelna.texi b/gcc/gelna.texi
new file mode 100644
index 0000000..e4bc6ce
--- /dev/null
+++ b/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/lang-specs.h b/gcc/lang-specs.h
new file mode 100644
index 0000000..ac3611d
--- /dev/null
+++ b/gcc/lang-specs.h
@@ -0,0 +1,28 @@
+/* GCC driver specs for 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
+<http://www.gnu.org/licenses/>. */
+
+/* gcc/gcc.cc */
+{".elna", "@elna", nullptr, 0, 0},
+{"@elna",
+ "elna1 %i \
+ %{!Q:-quiet} " DUMPS_OPTIONS("") " %{m*} %{aux-info*} \
+ %{g*} %{O*} %{W*&pedantic*} %{w} %{std*&ansi&trigraphs} \
+ %{pg:-p} %{p} %{f*} %{undef} \
+ %{!fsyntax-only:%{S:%W{o*}%{!o*:-o %w%b.s}}} \
+ %{fsyntax-only:-o %j} %{-param*} \
+ %{!fsyntax-only:%(invoke_as)}",
+ nullptr, 0, 0},
diff --git a/gcc/lang.opt b/gcc/lang.opt
new file mode 100644
index 0000000..1c8c95c
--- /dev/null
+++ b/gcc/lang.opt
@@ -0,0 +1,23 @@
+; lang.opt -- Options for 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
+; <http://www.gnu.org/licenses/>.
+
+; See the GCC internals manual for a description of this file's format.
+
+; Please try to keep this file in ASCII collating order.
+
+Language
+Elna
diff --git a/gcc/lang.opt.urls b/gcc/lang.opt.urls
new file mode 100644
index 0000000..a383952
--- /dev/null
+++ b/gcc/lang.opt.urls
@@ -0,0 +1,2 @@
+; Autogenerated by regenerate-opt-urls.py from gcc/lang.opt and generated HTML
+