elna/gcc/elna-generic.cc

460 lines
18 KiB
C++

#include "elna/gcc/elna-generic.h"
#include "elna/gcc/elna-tree.h"
#include "elna/gcc/elna-diagnostic.h"
#include "input.h"
#include "cgraph.h"
#include "gimplify.h"
#include "stringpool.h"
#include "diagnostic.h"
#include "realmpfr.h"
namespace elna
{
namespace gcc
{
void generic_visitor::visit(source::call_statement *statement)
{
if (statement->name() != "writei")
{
error_at(get_location(&statement->position()),
"procedure '%s' not declared",
statement->name().c_str());
return;
}
if (statement->arguments().size() != 1)
{
error_at(get_location(&statement->position()),
"procedure '%s' expects 1 argument, %i given",
statement->name().c_str(), statement->arguments().size());
return;
}
auto& argument = statement->arguments().at(0);
argument->accept(this);
auto argument_type = TREE_TYPE(this->current_expression);
const char *format_number{ nullptr };
if (argument_type == integer_type_node)
{
format_number = "%d\n";
}
else if (argument_type == double_type_node)
{
format_number = "%f\n";
}
else
{
error_at(get_location(&argument->position()),
"invalid argument of type %s for procedure %s",
print_type(argument_type), statement->name().c_str());
this->current_expression = error_mark_node;
return;
}
tree args[] = {
build_string_literal(strlen(format_number) + 1, format_number),
this->current_expression
};
tree fndecl_type_param[] = {
build_pointer_type(build_qualified_type(char_type_node, TYPE_QUAL_CONST)) /* const char* */
};
tree fndecl_type = build_varargs_function_type_array(integer_type_node, 1, fndecl_type_param);
tree printf_fn_decl = build_fn_decl("printf", fndecl_type);
DECL_EXTERNAL(printf_fn_decl) = 1;
tree printf_fn = build1(ADDR_EXPR, build_pointer_type(fndecl_type), printf_fn_decl);
tree stmt = build_call_array(integer_type_node, printf_fn, 2, args);
append_to_statement_list(stmt, &this->current_statements);
this->current_expression = NULL_TREE;
}
void generic_visitor::visit(source::program *program)
{
tree main_fndecl_type_param[] = {
integer_type_node,
build_pointer_type(build_pointer_type(char_type_node))
};
tree main_fndecl_type = build_function_type_array(integer_type_node, 2, main_fndecl_type_param);
this->main_fndecl = build_fn_decl("main", main_fndecl_type);
tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, integer_type_node);
DECL_RESULT(this->main_fndecl) = resdecl;
tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(main_fndecl),
build_int_cst_type(integer_type_node, 0));
tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result);
this->current_statements = alloc_stmt_list();
empty_visitor::visit(program);
append_to_statement_list(return_stmt, &this->current_statements);
tree new_block = build_block(NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
tree bind_expr = build3(BIND_EXPR, void_type_node, NULL_TREE, this->current_statements, new_block);
BLOCK_SUPERCONTEXT(new_block) = this->main_fndecl;
DECL_INITIAL(this->main_fndecl) = new_block;
DECL_SAVED_TREE(this->main_fndecl) = bind_expr;
DECL_EXTERNAL(this->main_fndecl) = 0;
DECL_PRESERVE_P(this->main_fndecl) = 1;
gimplify_function_tree(this->main_fndecl);
cgraph_node::finalize_function(this->main_fndecl, true);
}
void generic_visitor::visit(source::number_literal<std::int32_t> *literal)
{
this->current_expression = build_int_cst_type(integer_type_node, literal->number());
}
void generic_visitor::visit(source::number_literal<double> *literal)
{
REAL_VALUE_TYPE real_value1;
mpfr_t number;
mpfr_init2(number, SIGNIFICAND_BITS);
mpfr_set_d(number, literal->number(), 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(source::boolean_literal *literal)
{
this->current_expression = build_int_cst_type(boolean_type_node, literal->boolean());
}
void generic_visitor::visit(source::binary_expression *expression)
{
expression->lhs().accept(this);
auto left = this->current_expression;
auto left_type = TREE_TYPE(left);
expression->rhs().accept(this);
auto right = this->current_expression;
auto right_type = TREE_TYPE(right);
auto expression_location = get_location(&expression->position());
tree_code operator_code = ERROR_MARK;
tree target_type = error_mark_node;
if (left_type != right_type)
{
error_at(expression_location,
"invalid operands of type %s and %s for operator %s",
print_type(left_type), print_type(right_type),
elna::source::print_binary_operator(expression->operation()));
this->current_expression = error_mark_node;
return;
}
switch (expression->operation())
{
case source::binary_operator::sum:
operator_code = PLUS_EXPR;
target_type = left_type;
break;
case source::binary_operator::subtraction:
operator_code = MINUS_EXPR;
target_type = left_type;
break;
case source::binary_operator::division:
operator_code = TRUNC_DIV_EXPR;
target_type = left_type;
break;
case source::binary_operator::multiplication:
operator_code = MULT_EXPR;
target_type = left_type;
break;
}
if (operator_code != ERROR_MARK) // An arithmetic operation.
{
if (target_type != integer_type_node && target_type != double_type_node)
{
error_at(expression_location,
"invalid operands of type %s and %s for operator %s",
print_type(left_type), print_type(right_type),
elna::source::print_binary_operator(expression->operation()));
this->current_expression = error_mark_node;
return;
}
}
switch (expression->operation())
{
case source::binary_operator::equals:
operator_code = EQ_EXPR;
target_type = boolean_type_node;
break;
case source::binary_operator::not_equals:
operator_code = NE_EXPR;
target_type = boolean_type_node;
break;
case source::binary_operator::less:
operator_code = LT_EXPR;
target_type = boolean_type_node;
break;
case source::binary_operator::greater:
operator_code = GT_EXPR;
target_type = boolean_type_node;
break;
case source::binary_operator::less_equal:
operator_code = LE_EXPR;
target_type = boolean_type_node;
break;
case source::binary_operator::greater_equal:
operator_code = GE_EXPR;
target_type = boolean_type_node;
break;
}
gcc_assert(operator_code != ERROR_MARK);
gcc_assert(target_type != error_mark_node);
this->current_expression = build2_loc(expression_location,
operator_code, target_type, left, right);
}
void generic_visitor::visit(source::constant_definition *definition)
{
location_t definition_location = get_location(&definition->position());
tree definition_tree = build_decl(definition_location, CONST_DECL,
get_identifier(definition->identifier().c_str()), integer_type_node);
auto result = this->symbol_map.insert({ definition->identifier(), definition_tree });
if (result.second)
{
definition->body().accept(this);
DECL_INITIAL(definition_tree) = build_int_cst_type(integer_type_node, definition->body().number());
TREE_CONSTANT(definition_tree) = 1;
TREE_READONLY(definition_tree) = 1;
auto declaration_statement = build1_loc(definition_location, DECL_EXPR,
void_type_node, definition_tree);
append_to_statement_list(declaration_statement, &this->current_statements);
}
else
{
error_at(definition_location,
"variable '%s' already declared in this scope",
definition->identifier().c_str());
}
this->current_expression = NULL_TREE;
}
void generic_visitor::visit(source::declaration *declaration)
{
tree declaration_type = error_mark_node;
if (declaration->type().base() == "Int")
{
declaration_type = integer_type_node;
}
else if (declaration->type().base() == "Bool")
{
declaration_type = boolean_type_node;
}
else if (declaration->type().base() == "Float")
{
declaration_type = double_type_node;
}
else
{
error_at(get_location(&declaration->type().position()),
"type '%s' not declared", declaration->type().base().c_str());
return;
}
auto declaration_location = get_location(&declaration->position());
tree declaration_tree = build_decl(declaration_location, VAR_DECL,
get_identifier(declaration->identifier().c_str()), declaration_type);
auto result = this->symbol_map.insert({ declaration->identifier(), declaration_tree });
if (result.second)
{
auto declaration_statement = build1_loc(declaration_location, DECL_EXPR,
void_type_node, declaration_tree);
append_to_statement_list(declaration_statement, &this->current_statements);
}
else
{
error_at(declaration_location,
"variable '%s' already declared in this scope",
declaration->identifier().c_str());
}
}
void generic_visitor::visit(source::variable_expression *expression)
{
auto symbol = this->symbol_map.find(expression->name());
if (symbol == this->symbol_map.end())
{
error_at(get_location(&expression->position()),
"variable '%s' not declared in the current scope",
expression->name().c_str());
this->current_expression = error_mark_node;
return;
}
this->current_expression = symbol->second;
}
void generic_visitor::visit(source::assign_statement *statement)
{
auto lvalue = this->symbol_map.find(statement->lvalue());
auto statement_location = get_location(&statement->position());
if (lvalue == this->symbol_map.end())
{
error_at(statement_location,
"variable '%s' not declared in the current scope",
statement->lvalue().c_str());
return;
}
statement->rvalue().accept(this);
if (TREE_CODE(lvalue->second) == CONST_DECL)
{
error_at(statement_location, "cannot modify constant '%s'",
statement->lvalue().c_str());
this->current_expression = error_mark_node;
return;
}
if (TREE_TYPE(this->current_expression) != TREE_TYPE(lvalue->second))
{
error_at(statement_location,
"cannot assign value of type %s to variable '%s' of type %s",
print_type(TREE_TYPE(this->current_expression)),
statement->lvalue().c_str(),
print_type(TREE_TYPE(lvalue->second)));
this->current_expression = error_mark_node;
return;
}
auto assignment = build2_loc(statement_location, MODIFY_EXPR,
void_type_node, lvalue->second, this->current_expression);
append_to_statement_list(assignment, &this->current_statements);
this->current_expression = NULL_TREE;
}
void generic_visitor::visit(source::if_statement *statement)
{
statement->prerequisite().accept(this);
if (TREE_TYPE(this->current_expression) != boolean_type_node)
{
error_at(get_location(&statement->prerequisite().position()),
"expected expression of boolean type but its type is %s",
print_type(TREE_TYPE(this->current_expression)));
this->current_expression = error_mark_node;
return;
}
auto then_location = get_location(&statement->body().position());
auto prerequisite_location = get_location(&statement->prerequisite().position());
auto then_label_decl = build_label_decl("then", then_location);
auto endif_label_decl = build_label_decl("end_if", then_location);
auto goto_then = build1_loc(prerequisite_location, GOTO_EXPR,
void_type_node, then_label_decl);
auto goto_endif = build1_loc(prerequisite_location, GOTO_EXPR,
void_type_node, endif_label_decl);
tree else_label_decl = NULL_TREE;
tree goto_else_or_endif = NULL_TREE;
if (statement->alternative() != nullptr)
{
auto else_location = get_location(&statement->alternative()->position());
else_label_decl = build_label_decl("else", else_location);
goto_else_or_endif = build1_loc(else_location, GOTO_EXPR, void_type_node, else_label_decl);
}
else
{
goto_else_or_endif = goto_endif;
}
auto cond_expr = build3_loc(prerequisite_location, COND_EXPR,
void_type_node, this->current_expression, goto_then, goto_else_or_endif);
append_to_statement_list(cond_expr, &this->current_statements);
auto then_label_expr = build1_loc(then_location, LABEL_EXPR,
void_type_node, then_label_decl);
append_to_statement_list(then_label_expr, &this->current_statements);
statement->body().accept(this);
if (statement->alternative() != nullptr)
{
auto else_label_expr = build1(LABEL_EXPR, void_type_node, else_label_decl);
append_to_statement_list(else_label_expr, &this->current_statements);
statement->alternative()->accept(this);
}
auto endif_label_expr = build1(LABEL_EXPR, void_type_node, endif_label_decl);
append_to_statement_list(endif_label_expr, &this->current_statements);
this->current_expression = NULL_TREE;
}
tree generic_visitor::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) = this->main_fndecl;
return label_decl;
}
void generic_visitor::visit(source::while_statement *statement)
{
statement->prerequisite().accept(this);
if (TREE_TYPE(this->current_expression) != boolean_type_node)
{
error_at(get_location(&statement->prerequisite().position()),
"expected expression of boolean type but its type is %s",
print_type(TREE_TYPE(this->current_expression)));
this->current_expression = error_mark_node;
return;
}
auto prerequisite_location = get_location(&statement->prerequisite().position());
auto body_location = get_location(&statement->body().position());
auto prerequisite_label_decl = build_label_decl("while_check", prerequisite_location);
auto prerequisite_label_expr = build1_loc(prerequisite_location, LABEL_EXPR,
void_type_node, prerequisite_label_decl);
append_to_statement_list(prerequisite_label_expr, &this->current_statements);
auto body_label_decl = build_label_decl("while_body", body_location);
auto end_label_decl = build_label_decl("end_while", UNKNOWN_LOCATION);
auto goto_body = build1_loc(prerequisite_location, GOTO_EXPR,
void_type_node, body_label_decl);
auto goto_end = build1_loc(prerequisite_location, GOTO_EXPR,
void_type_node, end_label_decl);
auto cond_expr = build3_loc(prerequisite_location, COND_EXPR,
void_type_node, this->current_expression, goto_body, goto_end);
append_to_statement_list(cond_expr, &this->current_statements);
auto body_label_expr = build1_loc(body_location, LABEL_EXPR,
void_type_node, body_label_decl);
append_to_statement_list(body_label_expr, &this->current_statements);
statement->body().accept(this);
auto goto_check = build1(GOTO_EXPR, void_type_node, prerequisite_label_decl);
append_to_statement_list(goto_check, &this->current_statements);
auto endif_label_expr = build1(LABEL_EXPR, void_type_node, end_label_decl);
append_to_statement_list(endif_label_expr, &this->current_statements);
this->current_expression = NULL_TREE;
}
}
}