Commit 8a35e18d by Chris Manghane Committed by Ian Lance Taylor

compiler: Use backend interface for unary expressions.

	* go-gcc.cc (Gcc_backend::unary_expression): New function.

From-SVN: r207063
parent 8adcc78b
2014-01-24 Chris Manghane <cmang@google.com>
* go-gcc.cc (Gcc_backend::unary_expression): New function.
2014-01-16 Chris Manghane <cmang@google.com> 2014-01-16 Chris Manghane <cmang@google.com>
* go-gcc.cc (Gcc_backend::conditional_expression): Add btype * go-gcc.cc (Gcc_backend::conditional_expression): Add btype
......
...@@ -254,6 +254,9 @@ class Gcc_backend : public Backend ...@@ -254,6 +254,9 @@ class Gcc_backend : public Backend
Location); Location);
Bexpression* Bexpression*
unary_expression(Operator, Bexpression*, Location);
Bexpression*
binary_expression(Operator, Bexpression*, Bexpression*, Location); binary_expression(Operator, Bexpression*, Bexpression*, Location);
// Statements. // Statements.
...@@ -1081,6 +1084,47 @@ Gcc_backend::conditional_expression(Btype* btype, Bexpression* condition, ...@@ -1081,6 +1084,47 @@ Gcc_backend::conditional_expression(Btype* btype, Bexpression* condition,
return this->make_expression(ret); return this->make_expression(ret);
} }
// Return an expression for the unary operation OP EXPR.
Bexpression*
Gcc_backend::unary_expression(Operator op, Bexpression* expr, Location location)
{
tree expr_tree = expr->get_tree();
if (expr_tree == error_mark_node
|| TREE_TYPE(expr_tree) == error_mark_node)
return this->error_expression();
tree type_tree = TREE_TYPE(expr_tree);
enum tree_code code;
switch (op)
{
case OPERATOR_MINUS:
{
tree computed_type = excess_precision_type(type_tree);
if (computed_type != NULL_TREE)
{
expr_tree = convert(computed_type, expr_tree);
type_tree = computed_type;
}
code = NEGATE_EXPR;
break;
}
case OPERATOR_NOT:
code = TRUTH_NOT_EXPR;
break;
case OPERATOR_XOR:
code = BIT_NOT_EXPR;
break;
default:
gcc_unreachable();
break;
}
tree ret = fold_build1_loc(location.gcc_location(), code, type_tree,
expr_tree);
return this->make_expression(ret);
}
// Convert a gofrontend operator to an equivalent tree_code. // Convert a gofrontend operator to an equivalent tree_code.
static enum tree_code static enum tree_code
......
...@@ -298,6 +298,12 @@ class Backend ...@@ -298,6 +298,12 @@ class Backend
Bexpression* then_expr, Bexpression* else_expr, Bexpression* then_expr, Bexpression* else_expr,
Location) = 0; Location) = 0;
// Return an expression for the unary operation OP EXPR.
// Supported values of OP are (from operators.h):
// MINUS, NOT, XOR.
virtual Bexpression*
unary_expression(Operator op, Bexpression* expr, Location) = 0;
// Return an expression for the binary operation LEFT OP RIGHT. // Return an expression for the binary operation LEFT OP RIGHT.
// Supported values of OP are (from operators.h): // Supported values of OP are (from operators.h):
// EQEQ, NOTEQ, LT, LE, GT, GE, PLUS, MINUS, OR, XOR, MULT, DIV, MOD, // EQEQ, NOTEQ, LT, LE, GT, GE, PLUS, MINUS, OR, XOR, MULT, DIV, MOD,
......
...@@ -1464,6 +1464,10 @@ class Func_code_reference_expression : public Expression ...@@ -1464,6 +1464,10 @@ class Func_code_reference_expression : public Expression
do_traverse(Traverse*) do_traverse(Traverse*)
{ return TRAVERSE_CONTINUE; } { return TRAVERSE_CONTINUE; }
bool
do_is_immutable() const
{ return true; }
Type* Type*
do_type() do_type()
{ return Type::make_pointer_type(Type::make_void_type()); } { return Type::make_pointer_type(Type::make_void_type()); }
...@@ -2941,6 +2945,10 @@ class Nil_expression : public Expression ...@@ -2941,6 +2945,10 @@ class Nil_expression : public Expression
do_is_constant() const do_is_constant() const
{ return true; } { return true; }
bool
do_is_immutable() const
{ return true; }
Type* Type*
do_type() do_type()
{ return Type::make_nil_type(); } { return Type::make_nil_type(); }
...@@ -3682,10 +3690,17 @@ class Unary_expression : public Expression ...@@ -3682,10 +3690,17 @@ class Unary_expression : public Expression
Expression* Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int); do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Expression*
do_flatten(Gogo*, Named_object*, Statement_inserter*);
bool bool
do_is_constant() const; do_is_constant() const;
bool bool
do_is_immutable() const
{ return this->expr_->is_immutable(); }
bool
do_numeric_constant_value(Numeric_constant*) const; do_numeric_constant_value(Numeric_constant*) const;
Type* Type*
...@@ -3806,6 +3821,45 @@ Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) ...@@ -3806,6 +3821,45 @@ Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
return this; return this;
} }
// Flatten expression if a nil check must be performed and create temporary
// variables if necessary.
Expression*
Unary_expression::do_flatten(Gogo* gogo, Named_object*,
Statement_inserter* inserter)
{
Location location = this->location();
if (this->op_ == OPERATOR_MULT
&& !this->expr_->is_variable())
{
go_assert(this->expr_->type()->points_to() != NULL);
Type* ptype = this->expr_->type()->points_to();
if (!ptype->is_void_type())
{
Btype* pbtype = ptype->get_backend(gogo);
size_t s = gogo->backend()->type_size(pbtype);
if (s >= 4096 || this->issue_nil_check_)
{
Temporary_statement* temp =
Statement::make_temporary(NULL, this->expr_, location);
inserter->insert(temp);
this->expr_ =
Expression::make_temporary_reference(temp, location);
}
}
}
if (this->create_temp_ && !this->expr_->is_variable())
{
Temporary_statement* temp =
Statement::make_temporary(NULL, this->expr_, location);
inserter->insert(temp);
this->expr_ = Expression::make_temporary_reference(temp, location);
}
return this;
}
// Return whether a unary expression is a constant. // Return whether a unary expression is a constant.
bool bool
...@@ -3821,8 +3875,8 @@ Unary_expression::do_is_constant() const ...@@ -3821,8 +3875,8 @@ Unary_expression::do_is_constant() const
else if (this->op_ == OPERATOR_AND) else if (this->op_ == OPERATOR_AND)
{ {
// Taking the address of a variable is constant if it is a // Taking the address of a variable is constant if it is a
// global variable, not constant otherwise. In other cases // global variable, not constant otherwise. In other cases taking the
// taking the address is probably not a constant. // address is probably not a constant.
Var_expression* ve = this->expr_->var_expression(); Var_expression* ve = this->expr_->var_expression();
if (ve != NULL) if (ve != NULL)
{ {
...@@ -4151,58 +4205,40 @@ Unary_expression::do_get_tree(Translate_context* context) ...@@ -4151,58 +4205,40 @@ Unary_expression::do_get_tree(Translate_context* context)
{ {
Temporary_statement* temp = sut->temporary(); Temporary_statement* temp = sut->temporary();
Bvariable* bvar = temp->get_backend_variable(context); Bvariable* bvar = temp->get_backend_variable(context);
tree var_tree = var_to_tree(bvar); Bexpression* bvar_expr = gogo->backend()->var_expression(bvar, loc);
Expression* val = sut->expression();
tree val_tree = val->get_tree(context); Expression* val = sut->expression();
if (var_tree == error_mark_node || val_tree == error_mark_node) Bexpression* bval = tree_to_expr(val->get_tree(context));
return error_mark_node;
tree addr_tree = build_fold_addr_expr_loc(loc.gcc_location(), Bstatement* bassign =
var_tree); gogo->backend()->assignment_statement(bvar_expr, bval, loc);
return build2_loc(loc.gcc_location(), COMPOUND_EXPR, Bexpression* bvar_addr =
TREE_TYPE(addr_tree), gogo->backend()->address_expression(bvar_expr, loc);
build2_loc(sut->location().gcc_location(), Bexpression* ret =
MODIFY_EXPR, void_type_node, gogo->backend()->compound_expression(bassign, bvar_addr, loc);
var_tree, val_tree), return expr_to_tree(ret);
addr_tree);
} }
} }
Bexpression* ret;
tree expr = this->expr_->get_tree(context); tree expr = this->expr_->get_tree(context);
if (expr == error_mark_node) Bexpression* bexpr = tree_to_expr(expr);
return error_mark_node; Btype* btype = this->expr_->type()->get_backend(gogo);
switch (this->op_) switch (this->op_)
{ {
case OPERATOR_PLUS: case OPERATOR_PLUS:
return expr; ret = bexpr;
break;
case OPERATOR_MINUS: case OPERATOR_MINUS:
{ ret = gogo->backend()->unary_expression(this->op_, bexpr, loc);
tree type = TREE_TYPE(expr); ret = gogo->backend()->convert_expression(btype, ret, loc);
tree compute_type = excess_precision_type(type); break;
if (compute_type != NULL_TREE)
expr = ::convert(compute_type, expr);
tree ret = fold_build1_loc(loc.gcc_location(), NEGATE_EXPR,
(compute_type != NULL_TREE
? compute_type
: type),
expr);
if (compute_type != NULL_TREE)
ret = ::convert(type, ret);
return ret;
}
case OPERATOR_NOT: case OPERATOR_NOT:
if (TREE_CODE(TREE_TYPE(expr)) == BOOLEAN_TYPE)
return fold_build1_loc(loc.gcc_location(), TRUTH_NOT_EXPR,
TREE_TYPE(expr), expr);
else
return fold_build2_loc(loc.gcc_location(), NE_EXPR, boolean_type_node,
expr, build_int_cst(TREE_TYPE(expr), 0));
case OPERATOR_XOR: case OPERATOR_XOR:
return fold_build1_loc(loc.gcc_location(), BIT_NOT_EXPR, TREE_TYPE(expr), ret = gogo->backend()->unary_expression(this->op_, bexpr, loc);
expr); break;
case OPERATOR_AND: case OPERATOR_AND:
if (!this->create_temp_) if (!this->create_temp_)
...@@ -4211,127 +4247,91 @@ Unary_expression::do_get_tree(Translate_context* context) ...@@ -4211,127 +4247,91 @@ Unary_expression::do_get_tree(Translate_context* context)
// where we would see one should have been moved onto the // where we would see one should have been moved onto the
// heap at parse time. Taking the address of a nonconstant // heap at parse time. Taking the address of a nonconstant
// constructor will not do what the programmer expects. // constructor will not do what the programmer expects.
go_assert(TREE_CODE(expr) != CONSTRUCTOR || TREE_CONSTANT(expr));
go_assert(TREE_CODE(expr) != ADDR_EXPR); go_assert(!this->expr_->is_composite_literal()
|| this->expr_->is_immutable());
Unary_expression* ue = static_cast<Unary_expression*>(this->expr_);
go_assert(ue == NULL || ue->op() != OPERATOR_AND);
} }
// Build a decl for a constant constructor. // Build a decl for a constant constructor.
if (TREE_CODE(expr) == CONSTRUCTOR && TREE_CONSTANT(expr)) if ((this->expr_->is_composite_literal()
{ || this->expr_->string_expression() != NULL)
tree decl = build_decl(this->location().gcc_location(), VAR_DECL, && this->expr_->is_immutable())
create_tmp_var_name("C"), TREE_TYPE(expr)); {
DECL_EXTERNAL(decl) = 0; static unsigned int counter;
TREE_PUBLIC(decl) = 0; char buf[100];
TREE_READONLY(decl) = 1; snprintf(buf, sizeof buf, "C%u", counter);
TREE_CONSTANT(decl) = 1; ++counter;
TREE_STATIC(decl) = 1;
TREE_ADDRESSABLE(decl) = 1; Bvariable* decl =
DECL_ARTIFICIAL(decl) = 1; gogo->backend()->immutable_struct(buf, true, false, btype, loc);
DECL_INITIAL(decl) = expr; gogo->backend()->immutable_struct_set_init(decl, buf, true, false,
rest_of_decl_compilation(decl, 1, 0); btype, loc, bexpr);
expr = decl; bexpr = gogo->backend()->var_expression(decl, loc);
} }
if (this->create_temp_
&& !TREE_ADDRESSABLE(TREE_TYPE(expr))
&& (TREE_CODE(expr) == CONST_DECL || !DECL_P(expr))
&& TREE_CODE(expr) != INDIRECT_REF
&& TREE_CODE(expr) != COMPONENT_REF)
{
if (current_function_decl != NULL)
{
tree tmp = create_tmp_var(TREE_TYPE(expr), get_name(expr));
DECL_IGNORED_P(tmp) = 1;
DECL_INITIAL(tmp) = expr;
TREE_ADDRESSABLE(tmp) = 1;
return build2_loc(loc.gcc_location(), COMPOUND_EXPR,
build_pointer_type(TREE_TYPE(expr)),
build1_loc(loc.gcc_location(), DECL_EXPR,
void_type_node, tmp),
build_fold_addr_expr_loc(loc.gcc_location(),
tmp));
}
else
{
tree tmp = build_decl(loc.gcc_location(), VAR_DECL,
create_tmp_var_name("A"), TREE_TYPE(expr));
DECL_EXTERNAL(tmp) = 0;
TREE_PUBLIC(tmp) = 0;
TREE_STATIC(tmp) = 1;
DECL_ARTIFICIAL(tmp) = 1;
TREE_ADDRESSABLE(tmp) = 1;
tree make_tmp;
if (!TREE_CONSTANT(expr))
make_tmp = fold_build2_loc(loc.gcc_location(), INIT_EXPR,
void_type_node, tmp, expr);
else
{
TREE_READONLY(tmp) = 1;
TREE_CONSTANT(tmp) = 1;
DECL_INITIAL(tmp) = expr;
make_tmp = NULL_TREE;
}
rest_of_decl_compilation(tmp, 1, 0);
tree addr = build_fold_addr_expr_loc(loc.gcc_location(), tmp);
if (make_tmp == NULL_TREE)
return addr;
return build2_loc(loc.gcc_location(), COMPOUND_EXPR,
TREE_TYPE(addr), make_tmp, addr);
}
}
return build_fold_addr_expr_loc(loc.gcc_location(), expr); go_assert(!this->create_temp_ || this->expr_->is_variable());
ret = gogo->backend()->address_expression(bexpr, loc);
break;
case OPERATOR_MULT: case OPERATOR_MULT:
{ {
go_assert(POINTER_TYPE_P(TREE_TYPE(expr))); go_assert(this->expr_->type()->points_to() != NULL);
// If we are dereferencing the pointer to a large struct, we // If we are dereferencing the pointer to a large struct, we
// need to check for nil. We don't bother to check for small // need to check for nil. We don't bother to check for small
// structs because we expect the system to crash on a nil // structs because we expect the system to crash on a nil
// pointer dereference. However, if we know the address of this // pointer dereference. However, if we know the address of this
// expression is being taken, we must always check for nil. // expression is being taken, we must always check for nil.
tree target_type_tree = TREE_TYPE(TREE_TYPE(expr));
if (!VOID_TYPE_P(target_type_tree)) Type* ptype = this->expr_->type()->points_to();
Btype* pbtype = ptype->get_backend(gogo);
if (!ptype->is_void_type())
{ {
HOST_WIDE_INT s = int_size_in_bytes(target_type_tree); size_t s = gogo->backend()->type_size(pbtype);
if (s == -1 || s >= 4096 || this->issue_nil_check_) if (s >= 4096 || this->issue_nil_check_)
{ {
if (!DECL_P(expr)) go_assert(this->expr_->is_variable());
expr = save_expr(expr);
tree compare = fold_build2_loc(loc.gcc_location(), EQ_EXPR, Expression* nil_expr = Expression::make_nil(loc);
boolean_type_node, Bexpression* nil = tree_to_expr(nil_expr->get_tree(context));
expr, Bexpression* compare =
fold_convert(TREE_TYPE(expr), gogo->backend()->binary_expression(OPERATOR_EQEQ, bexpr,
null_pointer_node)); nil, loc);
Expression* crash_expr = Expression* crash_expr =
gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc); gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc);
tree crash = crash_expr->get_tree(context); Bexpression* crash =
expr = fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR, tree_to_expr(crash_expr->get_tree(context));
TREE_TYPE(expr), build3(COND_EXPR, bexpr = gogo->backend()->conditional_expression(btype, compare,
void_type_node, crash, bexpr,
compare, crash, loc);
NULL_TREE),
expr);
} }
} }
// If the type of EXPR is a recursive pointer type, then we // If the type of EXPR is a recursive pointer type, then we
// need to insert a cast before indirecting. // need to insert a cast before indirecting.
if (VOID_TYPE_P(target_type_tree)) tree expr = expr_to_tree(bexpr);
{ tree target_type_tree = TREE_TYPE(TREE_TYPE(expr));
Type* pt = this->expr_->type()->points_to(); if (VOID_TYPE_P(target_type_tree))
tree ind = type_to_tree(pt->get_backend(gogo)); {
expr = fold_convert_loc(loc.gcc_location(), tree ind = type_to_tree(pbtype);
expr = fold_convert_loc(loc.gcc_location(),
build_pointer_type(ind), expr); build_pointer_type(ind), expr);
} bexpr = tree_to_expr(expr);
}
return build_fold_indirect_ref_loc(loc.gcc_location(), expr); ret = gogo->backend()->indirect_expression(bexpr, false, loc);
} }
break;
default: default:
go_unreachable(); go_unreachable();
} }
return expr_to_tree(ret);
} }
// Export a unary expression. // Export a unary expression.
...@@ -12232,6 +12232,9 @@ class Struct_construction_expression : public Expression ...@@ -12232,6 +12232,9 @@ class Struct_construction_expression : public Expression
int int
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
bool
do_is_immutable() const;
Type* Type*
do_type() do_type()
{ return this->type_; } { return this->type_; }
...@@ -12334,6 +12337,23 @@ Struct_construction_expression::is_constant_struct() const ...@@ -12334,6 +12337,23 @@ Struct_construction_expression::is_constant_struct() const
return true; return true;
} }
// Return whether this struct is immutable.
bool
Struct_construction_expression::do_is_immutable() const
{
if (this->vals_ == NULL)
return true;
for (Expression_list::const_iterator pv = this->vals_->begin();
pv != this->vals_->end();
++pv)
{
if (*pv != NULL && !(*pv)->is_immutable())
return false;
}
return true;
}
// Final type determination. // Final type determination.
void void
...@@ -12546,6 +12566,9 @@ protected: ...@@ -12546,6 +12566,9 @@ protected:
int int
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
bool
do_is_immutable() const;
Type* Type*
do_type() do_type()
{ return this->type_; } { return this->type_; }
...@@ -12624,6 +12647,23 @@ Array_construction_expression::is_constant_array() const ...@@ -12624,6 +12647,23 @@ Array_construction_expression::is_constant_array() const
return true; return true;
} }
// Return whether this is an immutable array initializer.
bool
Array_construction_expression::do_is_immutable() const
{
if (this->vals_ == NULL)
return true;
for (Expression_list::const_iterator pv = this->vals_->begin();
pv != this->vals_->end();
++pv)
{
if (*pv != NULL && !(*pv)->is_immutable())
return false;
}
return true;
}
// Final type determination. // Final type determination.
void void
...@@ -14390,6 +14430,10 @@ class Type_descriptor_expression : public Expression ...@@ -14390,6 +14430,10 @@ class Type_descriptor_expression : public Expression
do_type() do_type()
{ return Type::make_type_descriptor_ptr_type(); } { return Type::make_type_descriptor_ptr_type(); }
bool
do_is_immutable() const
{ return true; }
void void
do_determine_type(const Type_context*) do_determine_type(const Type_context*)
{ } { }
......
...@@ -403,6 +403,11 @@ class Expression ...@@ -403,6 +403,11 @@ class Expression
is_constant() const is_constant() const
{ return this->do_is_constant(); } { return this->do_is_constant(); }
// Return whether this is an immutable expression.
bool
is_immutable() const
{ return this->do_is_immutable(); }
// If this is not a numeric constant, return false. If it is one, // If this is not a numeric constant, return false. If it is one,
// return true, and set VAL to hold the value. // return true, and set VAL to hold the value.
bool bool
...@@ -758,6 +763,11 @@ class Expression ...@@ -758,6 +763,11 @@ class Expression
do_is_constant() const do_is_constant() const
{ return false; } { return false; }
// Return whether this is an immutable expression.
virtual bool
do_is_immutable() const
{ return false; }
// Return whether this is a constant expression of numeric type, and // Return whether this is a constant expression of numeric type, and
// set the Numeric_constant to the value. // set the Numeric_constant to the value.
virtual bool virtual bool
...@@ -1196,6 +1206,10 @@ class String_expression : public Expression ...@@ -1196,6 +1206,10 @@ class String_expression : public Expression
{ return true; } { return true; }
bool bool
do_is_immutable() const
{ return true; }
bool
do_string_constant_value(std::string* val) const do_string_constant_value(std::string* val) const
{ {
*val = this->val_; *val = this->val_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment