246 lines
7.8 KiB
C++
246 lines
7.8 KiB
C++
/* elna-tree.cc -- 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 "stor-layout.h"
|
|
#include "fold-const.h"
|
|
#include "diagnostic-core.h"
|
|
#include "stringpool.h"
|
|
|
|
namespace elna
|
|
{
|
|
namespace gcc
|
|
{
|
|
bool is_pointer_type(tree type)
|
|
{
|
|
gcc_assert(TYPE_P(type));
|
|
return TREE_CODE(type) == POINTER_TYPE;
|
|
}
|
|
|
|
bool is_integral_type(tree type)
|
|
{
|
|
gcc_assert(TYPE_P(type));
|
|
return TREE_CODE(type) == INTEGER_TYPE;
|
|
}
|
|
|
|
bool are_compatible_pointers(tree lhs, tree rhs)
|
|
{
|
|
tree lhs_type = TREE_TYPE(lhs);
|
|
tree rhs_type = TREE_TYPE(rhs);
|
|
|
|
return (is_pointer_type(lhs_type) && rhs == null_pointer_node)
|
|
|| (is_pointer_type(lhs_type) && lhs_type == rhs_type);
|
|
}
|
|
|
|
tree tree_chain_base::head()
|
|
{
|
|
return first;
|
|
}
|
|
|
|
void tree_chain_base::append(tree t)
|
|
{
|
|
gcc_assert(t != NULL_TREE);
|
|
|
|
if (this->first == NULL_TREE)
|
|
{
|
|
this->first = this->last = t;
|
|
}
|
|
else
|
|
{
|
|
chain(t);
|
|
this->last = t;
|
|
}
|
|
}
|
|
|
|
void tree_chain::chain(tree t)
|
|
{
|
|
TREE_CHAIN(this->last) = t;
|
|
}
|
|
|
|
void block_chain::chain(tree t)
|
|
{
|
|
BLOCK_CHAIN(this->last) = t;
|
|
}
|
|
|
|
tree_symbol_mapping::tree_symbol_mapping(tree bind_expression, tree block)
|
|
: m_bind_expression(bind_expression), m_block(block)
|
|
{
|
|
}
|
|
|
|
tree tree_symbol_mapping::bind_expression()
|
|
{
|
|
return m_bind_expression;
|
|
}
|
|
|
|
tree tree_symbol_mapping::block()
|
|
{
|
|
return m_block;
|
|
}
|
|
|
|
block_scope::block_scope()
|
|
: m_statement_list(alloc_stmt_list())
|
|
{
|
|
}
|
|
|
|
void block_scope::append_statement(tree statement_tree)
|
|
{
|
|
if (!defers.empty())
|
|
{
|
|
append_to_statement_list(statement_tree, &this->defers.front().second);
|
|
}
|
|
else
|
|
{
|
|
append_to_statement_list(statement_tree, &this->m_statement_list);
|
|
}
|
|
}
|
|
|
|
void block_scope::defer(tree statement_tree)
|
|
{
|
|
defers.push_front({ statement_tree, alloc_stmt_list() });
|
|
}
|
|
|
|
tree block_scope::chain_defer()
|
|
{
|
|
if (this->defers.empty())
|
|
{
|
|
return m_statement_list;
|
|
}
|
|
std::forward_list<std::pair<tree, tree>>::iterator defer_iterator =
|
|
this->defers.begin();
|
|
tree defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, defer_iterator->second, defer_iterator->first);
|
|
|
|
++defer_iterator;
|
|
for (; defer_iterator != this->defers.end(); ++defer_iterator)
|
|
{
|
|
append_to_statement_list(defer_tree, &defer_iterator->second);
|
|
defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, defer_iterator->second, defer_iterator->first);
|
|
}
|
|
return build2(COMPOUND_EXPR, TREE_TYPE(defer_tree), m_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;
|
|
}
|
|
|
|
std::shared_ptr<boot::symbol_table<tree>> builtin_symbol_table()
|
|
{
|
|
std::shared_ptr<boot::symbol_table<tree>> initial_table =
|
|
std::make_shared<boot::symbol_table<tree>>();
|
|
|
|
initial_table->enter("Int", long_integer_type_node);
|
|
initial_table->enter("Word", size_type_node);
|
|
initial_table->enter("Bool", boolean_type_node);
|
|
initial_table->enter("Float", double_type_node);
|
|
initial_table->enter("Char", unsigned_char_type_node);
|
|
initial_table->enter("Byte", make_unsigned_type(8));
|
|
|
|
tree string_record = make_node(RECORD_TYPE);
|
|
tree_chain record_chain;
|
|
|
|
record_chain.append(build_field(UNKNOWN_LOCATION, string_record, "length", initial_table->lookup("Word")));
|
|
record_chain.append(build_field(UNKNOWN_LOCATION, string_record, "ptr",
|
|
build_pointer_type_for_mode(initial_table->lookup("Char"), VOIDmode, true)));
|
|
|
|
TYPE_FIELDS(string_record) = record_chain.head();
|
|
layout_type(string_record);
|
|
|
|
initial_table->enter("String", string_record);
|
|
|
|
return initial_table;
|
|
}
|
|
|
|
tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right)
|
|
{
|
|
if (binary_operator == boot::binary_operator::sum)
|
|
{
|
|
tree pointer{ NULL_TREE };
|
|
tree offset{ NULL_TREE };
|
|
|
|
if (is_pointer_type(TREE_TYPE(left)) && is_integral_type(TREE_TYPE(right)))
|
|
{
|
|
pointer = left;
|
|
offset = right;
|
|
}
|
|
else if (is_integral_type(TREE_TYPE(left)) && is_pointer_type(TREE_TYPE(right)))
|
|
{
|
|
pointer = right;
|
|
offset = left;
|
|
}
|
|
else
|
|
{
|
|
return error_mark_node;
|
|
}
|
|
tree size_exp = fold_convert(TREE_TYPE(offset), size_in_bytes(TREE_TYPE(TREE_TYPE(pointer))));
|
|
|
|
offset = fold_build2(MULT_EXPR, TREE_TYPE(offset), offset, size_exp);
|
|
offset = fold_convert(sizetype, offset);
|
|
|
|
return fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(pointer), pointer, offset);
|
|
}
|
|
else if (binary_operator == boot::binary_operator::subtraction)
|
|
{
|
|
if (is_pointer_type(TREE_TYPE(left)) && is_integral_type(TREE_TYPE(right)))
|
|
{
|
|
tree pointer_type = TREE_TYPE(left);
|
|
tree offset_type = TREE_TYPE(right);
|
|
tree size_exp = fold_convert(offset_type, size_in_bytes(TREE_TYPE(pointer_type)));
|
|
|
|
tree convert_expression = fold_build2(MULT_EXPR, offset_type, right, size_exp);
|
|
convert_expression = fold_convert(sizetype, convert_expression);
|
|
|
|
convert_expression = fold_build1(NEGATE_EXPR, sizetype, convert_expression);
|
|
return fold_build2(POINTER_PLUS_EXPR, pointer_type, left, convert_expression);
|
|
} else if (is_pointer_type(TREE_TYPE(left)) && is_pointer_type(TREE_TYPE(right))
|
|
&& TREE_TYPE(left) == TREE_TYPE(right))
|
|
{
|
|
return fold_build2(POINTER_DIFF_EXPR, ssizetype, left, right);
|
|
}
|
|
}
|
|
return error_mark_node;
|
|
}
|
|
|
|
tree build_binary_operation(bool condition, boot::binary_expression *expression,
|
|
tree_code operator_code, tree left, tree right, tree target_type)
|
|
{
|
|
location_t expression_location = get_location(&expression->position());
|
|
tree left_type = TREE_TYPE(left);
|
|
tree right_type = TREE_TYPE(right);
|
|
|
|
if (condition)
|
|
{
|
|
return 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::boot::print_binary_operator(expression->operation()));
|
|
return error_mark_node;
|
|
}
|
|
}
|
|
}
|
|
}
|