diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index f866bdc018b..4d2fbfd5a58 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -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); diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 47ce4c0a1e5..e447418b949 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -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*);