Commit 58c55a32 by Ian Lance Taylor

compiler: Use backend interface for comparisons.

From-SVN: r204827
parent 1726bd6e
...@@ -5321,7 +5321,7 @@ Binary_expression::do_lower(Gogo* gogo, Named_object*, ...@@ -5321,7 +5321,7 @@ Binary_expression::do_lower(Gogo* gogo, Named_object*,
} }
} }
// Lower struct and array comparisons. // Lower struct, array, and some interface comparisons.
if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ) if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ)
{ {
if (left->type()->struct_type() != NULL) if (left->type()->struct_type() != NULL)
...@@ -5329,6 +5329,11 @@ Binary_expression::do_lower(Gogo* gogo, Named_object*, ...@@ -5329,6 +5329,11 @@ Binary_expression::do_lower(Gogo* gogo, Named_object*,
else if (left->type()->array_type() != NULL else if (left->type()->array_type() != NULL
&& !left->type()->is_slice_type()) && !left->type()->is_slice_type())
return this->lower_array_comparison(gogo, inserter); return this->lower_array_comparison(gogo, inserter);
else if ((left->type()->interface_type() != NULL
&& right->type()->interface_type() == NULL)
|| (left->type()->interface_type() == NULL
&& right->type()->interface_type() != NULL))
return this->lower_interface_value_comparison(gogo, inserter);
} }
return this; return this;
...@@ -5457,6 +5462,57 @@ Binary_expression::lower_array_comparison(Gogo* gogo, ...@@ -5457,6 +5462,57 @@ Binary_expression::lower_array_comparison(Gogo* gogo,
return ret; return ret;
} }
// Lower an interface to value comparison.
Expression*
Binary_expression::lower_interface_value_comparison(Gogo*,
Statement_inserter* inserter)
{
Type* left_type = this->left_->type();
Type* right_type = this->right_->type();
Interface_type* ift;
if (left_type->interface_type() != NULL)
{
ift = left_type->interface_type();
if (!ift->implements_interface(right_type, NULL))
return this;
}
else
{
ift = right_type->interface_type();
if (!ift->implements_interface(left_type, NULL))
return this;
}
if (!Type::are_compatible_for_comparison(true, left_type, right_type, NULL))
return this;
Location loc = this->location();
if (left_type->interface_type() == NULL
&& left_type->points_to() == NULL
&& !this->left_->is_addressable())
{
Temporary_statement* temp =
Statement::make_temporary(left_type, NULL, loc);
inserter->insert(temp);
this->left_ =
Expression::make_set_and_use_temporary(temp, this->left_, loc);
}
if (right_type->interface_type() == NULL
&& right_type->points_to() == NULL
&& !this->right_->is_addressable())
{
Temporary_statement* temp =
Statement::make_temporary(right_type, NULL, loc);
inserter->insert(temp);
this->right_ =
Expression::make_set_and_use_temporary(temp, this->right_, loc);
}
return this;
}
// Lower a struct or array comparison to a call to memcmp. // Lower a struct or array comparison to a call to memcmp.
Expression* Expression*
...@@ -5919,8 +5975,7 @@ Binary_expression::do_get_tree(Translate_context* context) ...@@ -5919,8 +5975,7 @@ Binary_expression::do_get_tree(Translate_context* context)
case OPERATOR_GT: case OPERATOR_GT:
case OPERATOR_GE: case OPERATOR_GE:
return Expression::comparison_tree(context, this->type_, this->op_, return Expression::comparison_tree(context, this->type_, this->op_,
this->left_->type(), left, this->left_, this->right_,
this->right_->type(), right,
this->location()); this->location());
case OPERATOR_OROR: case OPERATOR_OROR:
...@@ -6417,12 +6472,16 @@ Expression::make_binary(Operator op, Expression* left, Expression* right, ...@@ -6417,12 +6472,16 @@ Expression::make_binary(Operator op, Expression* left, Expression* right,
tree tree
Expression::comparison_tree(Translate_context* context, Type* result_type, Expression::comparison_tree(Translate_context* context, Type* result_type,
Operator op, Type* left_type, tree left_tree, Operator op, Expression* left_expr,
Type* right_type, tree right_tree, Expression* right_expr, Location location)
Location location)
{ {
Type* int_type = Type::lookup_integer_type("int"); Type* left_type = left_expr->type();
tree int_type_tree = type_to_tree(int_type->get_backend(context->gogo())); Type* right_type = right_expr->type();
mpz_t zval;
mpz_init_set_ui(zval, 0UL);
Expression* zexpr = Expression::make_integer(&zval, NULL, location);
mpz_clear(zval);
enum tree_code code; enum tree_code code;
switch (op) switch (op)
...@@ -6449,21 +6508,17 @@ Expression::comparison_tree(Translate_context* context, Type* result_type, ...@@ -6449,21 +6508,17 @@ Expression::comparison_tree(Translate_context* context, Type* result_type,
go_unreachable(); go_unreachable();
} }
// FIXME: Computing the tree here means it will be computed multiple times,
// which is wasteful. This is a temporary modification until all tree code
// here can be replaced with frontend expressions.
tree left_tree = left_expr->get_tree(context);
tree right_tree = right_expr->get_tree(context);
if (left_type->is_string_type() && right_type->is_string_type()) if (left_type->is_string_type() && right_type->is_string_type())
{ {
Type* st = Type::make_string_type(); Expression* strcmp_call = Runtime::make_call(Runtime::STRCMP, location, 2,
tree string_type = type_to_tree(st->get_backend(context->gogo())); left_expr, right_expr);
static tree string_compare_decl; left_tree = strcmp_call->get_tree(context);
left_tree = Gogo::call_builtin(&string_compare_decl, right_tree = zexpr->get_tree(context);
location,
"__go_strcmp",
2,
int_type_tree,
string_type,
left_tree,
string_type,
right_tree);
right_tree = build_int_cst_type(int_type_tree, 0);
} }
else if ((left_type->interface_type() != NULL else if ((left_type->interface_type() != NULL
&& right_type->interface_type() == NULL && right_type->interface_type() == NULL
...@@ -6476,154 +6531,61 @@ Expression::comparison_tree(Translate_context* context, Type* result_type, ...@@ -6476,154 +6531,61 @@ Expression::comparison_tree(Translate_context* context, Type* result_type,
if (left_type->interface_type() == NULL) if (left_type->interface_type() == NULL)
{ {
std::swap(left_type, right_type); std::swap(left_type, right_type);
std::swap(left_tree, right_tree); std::swap(left_expr, right_expr);
} }
// The right operand is not an interface. We need to take its // The right operand is not an interface. We need to take its
// address if it is not a pointer. // address if it is not a pointer.
tree make_tmp; Expression* pointer_arg = NULL;
tree arg;
if (right_type->points_to() != NULL) if (right_type->points_to() != NULL)
{ pointer_arg = right_expr;
make_tmp = NULL_TREE;
arg = right_tree;
}
else if (TREE_ADDRESSABLE(TREE_TYPE(right_tree))
|| (TREE_CODE(right_tree) != CONST_DECL
&& DECL_P(right_tree)))
{
make_tmp = NULL_TREE;
arg = build_fold_addr_expr_loc(location.gcc_location(), right_tree);
if (DECL_P(right_tree))
TREE_ADDRESSABLE(right_tree) = 1;
}
else
{
tree tmp = create_tmp_var(TREE_TYPE(right_tree),
get_name(right_tree));
DECL_IGNORED_P(tmp) = 0;
DECL_INITIAL(tmp) = right_tree;
TREE_ADDRESSABLE(tmp) = 1;
make_tmp = build1(DECL_EXPR, void_type_node, tmp);
SET_EXPR_LOCATION(make_tmp, location.gcc_location());
arg = build_fold_addr_expr_loc(location.gcc_location(), tmp);
}
arg = fold_convert_loc(location.gcc_location(), ptr_type_node, arg);
Bexpression* descriptor_bexpr =
right_type->type_descriptor_pointer(context->gogo(), location);
tree descriptor = expr_to_tree(descriptor_bexpr);
if (left_type->interface_type()->is_empty())
{
static tree empty_interface_value_compare_decl;
left_tree = Gogo::call_builtin(&empty_interface_value_compare_decl,
location,
"__go_empty_interface_value_compare",
3,
int_type_tree,
TREE_TYPE(left_tree),
left_tree,
TREE_TYPE(descriptor),
descriptor,
ptr_type_node,
arg);
if (left_tree == error_mark_node)
return error_mark_node;
// This can panic if the type is not comparable.
TREE_NOTHROW(empty_interface_value_compare_decl) = 0;
}
else else
{ {
static tree interface_value_compare_decl; go_assert(right_expr->is_addressable());
left_tree = Gogo::call_builtin(&interface_value_compare_decl, pointer_arg = Expression::make_unary(OPERATOR_AND, right_expr,
location, location);
"__go_interface_value_compare",
3,
int_type_tree,
TREE_TYPE(left_tree),
left_tree,
TREE_TYPE(descriptor),
descriptor,
ptr_type_node,
arg);
if (left_tree == error_mark_node)
return error_mark_node;
// This can panic if the type is not comparable.
TREE_NOTHROW(interface_value_compare_decl) = 0;
} }
right_tree = build_int_cst_type(int_type_tree, 0);
if (make_tmp != NULL_TREE) Expression* descriptor_expr = Expression::make_type_descriptor(right_type,
left_tree = build2(COMPOUND_EXPR, TREE_TYPE(left_tree), make_tmp, location);
left_tree); Call_expression* iface_valcmp =
Runtime::make_call((left_type->interface_type()->is_empty()
? Runtime::EMPTY_INTERFACE_VALUE_COMPARE
: Runtime::INTERFACE_VALUE_COMPARE),
location, 3, left_expr, descriptor_expr,
pointer_arg);
left_tree = iface_valcmp->get_tree(context);
right_tree = zexpr->get_tree(context);
} }
else if (left_type->interface_type() != NULL else if (left_type->interface_type() != NULL
&& right_type->interface_type() != NULL) && right_type->interface_type() != NULL)
{ {
Runtime::Function compare_function;
if (left_type->interface_type()->is_empty() if (left_type->interface_type()->is_empty()
&& right_type->interface_type()->is_empty()) && right_type->interface_type()->is_empty())
{ compare_function = Runtime::EMPTY_INTERFACE_COMPARE;
static tree empty_interface_compare_decl;
left_tree = Gogo::call_builtin(&empty_interface_compare_decl,
location,
"__go_empty_interface_compare",
2,
int_type_tree,
TREE_TYPE(left_tree),
left_tree,
TREE_TYPE(right_tree),
right_tree);
if (left_tree == error_mark_node)
return error_mark_node;
// This can panic if the type is uncomparable.
TREE_NOTHROW(empty_interface_compare_decl) = 0;
}
else if (!left_type->interface_type()->is_empty() else if (!left_type->interface_type()->is_empty()
&& !right_type->interface_type()->is_empty()) && !right_type->interface_type()->is_empty())
{ compare_function = Runtime::INTERFACE_COMPARE;
static tree interface_compare_decl;
left_tree = Gogo::call_builtin(&interface_compare_decl,
location,
"__go_interface_compare",
2,
int_type_tree,
TREE_TYPE(left_tree),
left_tree,
TREE_TYPE(right_tree),
right_tree);
if (left_tree == error_mark_node)
return error_mark_node;
// This can panic if the type is uncomparable.
TREE_NOTHROW(interface_compare_decl) = 0;
}
else else
{ {
if (left_type->interface_type()->is_empty()) if (left_type->interface_type()->is_empty())
{ {
go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ); go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ);
std::swap(left_type, right_type); std::swap(left_type, right_type);
std::swap(left_tree, right_tree); std::swap(left_expr, right_expr);
} }
go_assert(!left_type->interface_type()->is_empty()); go_assert(!left_type->interface_type()->is_empty());
go_assert(right_type->interface_type()->is_empty()); go_assert(right_type->interface_type()->is_empty());
static tree interface_empty_compare_decl; compare_function = Runtime::INTERFACE_EMPTY_COMPARE;
left_tree = Gogo::call_builtin(&interface_empty_compare_decl,
location,
"__go_interface_empty_compare",
2,
int_type_tree,
TREE_TYPE(left_tree),
left_tree,
TREE_TYPE(right_tree),
right_tree);
if (left_tree == error_mark_node)
return error_mark_node;
// This can panic if the type is uncomparable.
TREE_NOTHROW(interface_empty_compare_decl) = 0;
} }
right_tree = build_int_cst_type(int_type_tree, 0); Call_expression* ifacecmp_call =
Runtime::make_call(compare_function, location, 2,
left_expr, right_expr);
left_tree = ifacecmp_call->get_tree(context);
right_tree = zexpr->get_tree(context);
} }
if (left_type->is_nil_type() if (left_type->is_nil_type()
...@@ -11908,14 +11870,11 @@ Interface_field_reference_expression::do_get_tree(Translate_context* context) ...@@ -11908,14 +11870,11 @@ Interface_field_reference_expression::do_get_tree(Translate_context* context)
// Note that we are evaluating this->expr_ twice, but that is OK // Note that we are evaluating this->expr_ twice, but that is OK
// because in the lowering pass we forced it into a temporary // because in the lowering pass we forced it into a temporary
// variable. // variable.
tree expr_tree = this->expr_->get_tree(context);
tree nil_check_tree = Expression::comparison_tree(context, tree nil_check_tree = Expression::comparison_tree(context,
Type::lookup_bool_type(), Type::lookup_bool_type(),
OPERATOR_EQEQ, OPERATOR_EQEQ,
this->expr_->type(), this->expr_,
expr_tree, Expression::make_nil(loc),
Type::make_nil_type(),
null_pointer_node,
loc); loc);
tree crash = context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, tree crash = context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
loc); loc);
......
...@@ -655,12 +655,11 @@ class Expression ...@@ -655,12 +655,11 @@ class Expression
Type* rhs_type, tree rhs_tree, Type* rhs_type, tree rhs_tree,
bool for_type_guard, Location); bool for_type_guard, Location);
// Return a tree implementing the comparison LHS_TREE OP RHS_TREE. // Return a tree implementing the comparison LHS_EXPR OP RHS_EXPR.
// TYPE is the type of both sides. // TYPE is the type of both sides.
static tree static tree
comparison_tree(Translate_context*, Type* result_type, Operator op, comparison_tree(Translate_context*, Type* result_type, Operator op,
Type* left_type, tree left_tree, Type* right_type, Expression* left_expr, Expression* right_expr, Location);
tree right_tree, Location);
// Return the backend expression for the numeric constant VAL. // Return the backend expression for the numeric constant VAL.
static Bexpression* static Bexpression*
...@@ -1306,6 +1305,9 @@ class Binary_expression : public Expression ...@@ -1306,6 +1305,9 @@ class Binary_expression : public Expression
lower_array_comparison(Gogo*, Statement_inserter*); lower_array_comparison(Gogo*, Statement_inserter*);
Expression* Expression*
lower_interface_value_comparison(Gogo*, Statement_inserter*);
Expression*
lower_compare_to_memcmp(Gogo*, Statement_inserter*); lower_compare_to_memcmp(Gogo*, Statement_inserter*);
Expression* Expression*
......
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