271 lines
10 KiB
C++
271 lines
10 KiB
C++
/* 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);
|
|
|
|
TREE_PUBLIC(type_declaration) = 1;
|
|
|
|
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_type_declaration(const std::string& identifier, tree type)
|
|
{
|
|
tree definition_tree = build_decl(UNKNOWN_LOCATION, TYPE_DECL,
|
|
get_identifier(identifier.c_str()), type);
|
|
|
|
TREE_PUBLIC(definition_tree) = true;
|
|
if (is_unique_type(type))
|
|
{
|
|
TYPE_NAME(type) = DECL_NAME(definition_tree);
|
|
TYPE_STUB_DECL(type) = definition_tree;
|
|
}
|
|
else
|
|
{
|
|
TYPE_NAME(type) = definition_tree;
|
|
}
|
|
return definition_tree;
|
|
}
|
|
|
|
tree build_composite_type(const std::vector<boot::type_field>& fields, tree composite_type_node,
|
|
std::shared_ptr<symbol_table> symbols, std::vector<std::string>& path)
|
|
{
|
|
for (auto& field : fields)
|
|
{
|
|
tree rewritten_field = get_inner_alias(field.second, symbols, path);
|
|
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<symbol_table> symbols,
|
|
std::vector<std::string>& path)
|
|
{
|
|
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, path);
|
|
}
|
|
tree return_type = void_type_node;
|
|
|
|
if (!procedure.return_type.proper_type.empty())
|
|
{
|
|
return_type = get_inner_alias(procedure.return_type.proper_type, symbols, path);
|
|
}
|
|
return build_function_type_array(return_type, procedure.parameters.size(), parameter_types.data());
|
|
}
|
|
|
|
tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols, std::vector<std::string>& path)
|
|
{
|
|
if (auto reference = type.get<boot::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<boot::record_type>())
|
|
{
|
|
tree composite_type_node = make_node(RECORD_TYPE);
|
|
|
|
build_composite_type(reference->fields, composite_type_node, symbols, path);
|
|
|
|
return composite_type_node;
|
|
}
|
|
else if (auto reference = type.get<boot::union_type>())
|
|
{
|
|
tree composite_type_node = make_node(UNION_TYPE);
|
|
|
|
build_composite_type(reference->fields, composite_type_node, symbols, path);
|
|
|
|
return composite_type_node;
|
|
}
|
|
else if (auto reference = type.get<boot::enumeration_type>())
|
|
{
|
|
return build_enumeration_type(reference->members);
|
|
}
|
|
else if (auto reference = type.get<boot::pointer_type>())
|
|
{
|
|
return build_global_pointer_type(get_inner_alias(reference->base, symbols, path));
|
|
}
|
|
else if (auto reference = type.get<boot::array_type>())
|
|
{
|
|
tree base = get_inner_alias(reference->base, symbols, path);
|
|
|
|
return build_static_array_type(base, reference->size);
|
|
}
|
|
else if (auto reference = type.get<boot::procedure_type>())
|
|
{
|
|
auto procedure = build_procedure_type(*reference, symbols, path);
|
|
|
|
return build_global_pointer_type(procedure);
|
|
}
|
|
else if (auto reference = type.get<boot::alias_type>())
|
|
{
|
|
return handle_symbol(reference->name, reference, symbols, path);
|
|
}
|
|
return error_mark_node;
|
|
}
|
|
|
|
tree handle_symbol(const std::string& symbol_name, std::shared_ptr<boot::alias_type> reference,
|
|
std::shared_ptr<symbol_table> symbols, std::vector<std::string>& path)
|
|
{
|
|
path.push_back(symbol_name);
|
|
std::vector<std::string>::const_iterator head_peace = std::cbegin(path);
|
|
|
|
if (std::find(std::next(head_peace), std::cend(path), *head_peace) != std::cend(path))
|
|
{
|
|
return error_mark_node;
|
|
}
|
|
tree looked_up = symbols->lookup(symbol_name);
|
|
|
|
if (looked_up == NULL_TREE)
|
|
{
|
|
looked_up = get_inner_alias(reference->reference, symbols, path);
|
|
|
|
symbols->enter(symbol_name, build_type_declaration(symbol_name, looked_up));
|
|
}
|
|
else
|
|
{
|
|
looked_up = TREE_TYPE(looked_up);
|
|
}
|
|
return looked_up;
|
|
}
|
|
|
|
void declare_procedure(const std::string& name, const boot::procedure_info& info,
|
|
std::shared_ptr<symbol_table> symbols)
|
|
{
|
|
std::vector<std::string> path;
|
|
tree declaration_type = gcc::build_procedure_type(info.symbol, symbols, path);
|
|
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<std::string>::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.symbols == nullptr;
|
|
}
|
|
|
|
void do_semantic_analysis(std::shared_ptr<boot::symbol_table> info_table, std::shared_ptr<symbol_table> symbols)
|
|
{
|
|
for (auto& [symbol_name, symbol_info] : *info_table)
|
|
{
|
|
std::vector<std::string> type_path;
|
|
|
|
if (auto type_info = symbol_info->is_type())
|
|
{
|
|
// The top level symbol table has basic (builtin) types in it which are not aliases.
|
|
if (auto alias_type = type_info->symbol.get<boot::alias_type>())
|
|
{
|
|
handle_symbol(symbol_name, alias_type, symbols, type_path);
|
|
}
|
|
}
|
|
else if (auto procedure_info = symbol_info->is_procedure())
|
|
{
|
|
declare_procedure(symbol_name, *procedure_info, symbols);
|
|
}
|
|
}
|
|
}
|
|
}
|