compiler: Use backend interface for comparisons.

From-SVN: r204827
This commit is contained in:
Ian Lance Taylor 2013-11-14 22:13:41 +00:00
parent 1726bd6ea3
commit 58c55a329a
2 changed files with 110 additions and 149 deletions

View File

@ -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 (left->type()->struct_type() != NULL)
@ -5329,6 +5329,11 @@ Binary_expression::do_lower(Gogo* gogo, Named_object*,
else if (left->type()->array_type() != NULL
&& !left->type()->is_slice_type())
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;
@ -5457,6 +5462,57 @@ Binary_expression::lower_array_comparison(Gogo* gogo,
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.
Expression*
@ -5919,8 +5975,7 @@ Binary_expression::do_get_tree(Translate_context* context)
case OPERATOR_GT:
case OPERATOR_GE:
return Expression::comparison_tree(context, this->type_, this->op_,
this->left_->type(), left,
this->right_->type(), right,
this->left_, this->right_,
this->location());
case OPERATOR_OROR:
@ -6417,12 +6472,16 @@ Expression::make_binary(Operator op, Expression* left, Expression* right,
tree
Expression::comparison_tree(Translate_context* context, Type* result_type,
Operator op, Type* left_type, tree left_tree,
Type* right_type, tree right_tree,
Location location)
Operator op, Expression* left_expr,
Expression* right_expr, Location location)
{
Type* int_type = Type::lookup_integer_type("int");
tree int_type_tree = type_to_tree(int_type->get_backend(context->gogo()));
Type* left_type = left_expr->type();
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;
switch (op)
@ -6449,21 +6508,17 @@ Expression::comparison_tree(Translate_context* context, Type* result_type,
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())
{
Type* st = Type::make_string_type();
tree string_type = type_to_tree(st->get_backend(context->gogo()));
static tree string_compare_decl;
left_tree = Gogo::call_builtin(&string_compare_decl,
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);
Expression* strcmp_call = Runtime::make_call(Runtime::STRCMP, location, 2,
left_expr, right_expr);
left_tree = strcmp_call->get_tree(context);
right_tree = zexpr->get_tree(context);
}
else if ((left_type->interface_type() != NULL
&& right_type->interface_type() == NULL
@ -6476,154 +6531,61 @@ Expression::comparison_tree(Translate_context* context, Type* result_type,
if (left_type->interface_type() == NULL)
{
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
// address if it is not a pointer.
tree make_tmp;
tree arg;
Expression* pointer_arg = NULL;
if (right_type->points_to() != NULL)
{
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;
}
pointer_arg = right_expr;
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);
go_assert(right_expr->is_addressable());
pointer_arg = Expression::make_unary(OPERATOR_AND, right_expr,
location);
}
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
{
static tree interface_value_compare_decl;
left_tree = Gogo::call_builtin(&interface_value_compare_decl,
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)
left_tree = build2(COMPOUND_EXPR, TREE_TYPE(left_tree), make_tmp,
left_tree);
Expression* descriptor_expr = Expression::make_type_descriptor(right_type,
location);
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
&& right_type->interface_type() != NULL)
{
Runtime::Function compare_function;
if (left_type->interface_type()->is_empty()
&& right_type->interface_type()->is_empty())
{
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;
}
compare_function = Runtime::EMPTY_INTERFACE_COMPARE;
else if (!left_type->interface_type()->is_empty()
&& !right_type->interface_type()->is_empty())
{
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;
}
compare_function = Runtime::INTERFACE_COMPARE;
else
{
if (left_type->interface_type()->is_empty())
{
go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ);
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(right_type->interface_type()->is_empty());
static tree interface_empty_compare_decl;
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;
compare_function = Runtime::INTERFACE_EMPTY_COMPARE;
}
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()
@ -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
// because in the lowering pass we forced it into a temporary
// variable.
tree expr_tree = this->expr_->get_tree(context);
tree nil_check_tree = Expression::comparison_tree(context,
Type::lookup_bool_type(),
OPERATOR_EQEQ,
this->expr_->type(),
expr_tree,
Type::make_nil_type(),
null_pointer_node,
this->expr_,
Expression::make_nil(loc),
loc);
tree crash = context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
loc);

View File

@ -655,12 +655,11 @@ class Expression
Type* rhs_type, tree rhs_tree,
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.
static tree
comparison_tree(Translate_context*, Type* result_type, Operator op,
Type* left_type, tree left_tree, Type* right_type,
tree right_tree, Location);
Expression* left_expr, Expression* right_expr, Location);
// Return the backend expression for the numeric constant VAL.
static Bexpression*
@ -1305,6 +1304,9 @@ class Binary_expression : public Expression
Expression*
lower_array_comparison(Gogo*, Statement_inserter*);
Expression*
lower_interface_value_comparison(Gogo*, Statement_inserter*);
Expression*
lower_compare_to_memcmp(Gogo*, Statement_inserter*);