This Go frontend patch from Chris Manghane uses the backend interface for interface info and field expressions. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline.
Ian 2014-01-14 Chris Manghane <cm...@google.com> * go-gcc.cc (Gcc_backend::compound_expression): New function. (Gcc_backend::conditional_expression): New function.
Index: gcc/go/gofrontend/expressions.cc =================================================================== --- gcc/go/gofrontend/expressions.cc (revision 206509) +++ gcc/go/gofrontend/expressions.cc (working copy) @@ -6473,11 +6473,11 @@ Expression::make_binary(Operator op, Exp tree Expression::comparison_tree(Translate_context* context, Type* result_type, - Operator op, Expression* left_expr, - Expression* right_expr, Location location) + Operator op, Expression* left, Expression* right, + Location location) { - Type* left_type = left_expr->type(); - Type* right_type = right_expr->type(); + Type* left_type = left->type(); + Type* right_type = right->type(); mpz_t zval; mpz_init_set_ui(zval, 0UL); @@ -6509,17 +6509,11 @@ Expression::comparison_tree(Translate_co 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()) { - 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); + left = Runtime::make_call(Runtime::STRCMP, location, 2, + left, right); + right = zexpr; } else if ((left_type->interface_type() != NULL && right_type->interface_type() == NULL @@ -6532,31 +6526,30 @@ Expression::comparison_tree(Translate_co if (left_type->interface_type() == NULL) { std::swap(left_type, right_type); - std::swap(left_expr, right_expr); + std::swap(left, right); } // The right operand is not an interface. We need to take its // address if it is not a pointer. Expression* pointer_arg = NULL; if (right_type->points_to() != NULL) - pointer_arg = right_expr; + pointer_arg = right; else { - go_assert(right_expr->is_addressable()); - pointer_arg = Expression::make_unary(OPERATOR_AND, right_expr, + go_assert(right->is_addressable()); + pointer_arg = Expression::make_unary(OPERATOR_AND, right, location); } - Expression* descriptor_expr = Expression::make_type_descriptor(right_type, - location); - Call_expression* iface_valcmp = + Expression* descriptor = + Expression::make_type_descriptor(right_type, location); + left = Runtime::make_call((left_type->interface_type()->is_empty() ? Runtime::EMPTY_INTERFACE_VALUE_COMPARE : Runtime::INTERFACE_VALUE_COMPARE), - location, 3, left_expr, descriptor_expr, + location, 3, left, descriptor, pointer_arg); - left_tree = iface_valcmp->get_tree(context); - right_tree = zexpr->get_tree(context); + right = zexpr; } else if (left_type->interface_type() != NULL && right_type->interface_type() != NULL) @@ -6574,56 +6567,42 @@ Expression::comparison_tree(Translate_co { go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ); std::swap(left_type, right_type); - std::swap(left_expr, right_expr); + std::swap(left, right); } go_assert(!left_type->interface_type()->is_empty()); go_assert(right_type->interface_type()->is_empty()); compare_function = Runtime::INTERFACE_EMPTY_COMPARE; } - 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); + left = Runtime::make_call(compare_function, location, 2, left, right); + right = zexpr; } if (left_type->is_nil_type() && (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ)) { std::swap(left_type, right_type); - std::swap(left_tree, right_tree); - std::swap(left_expr, right_expr); + std::swap(left, right); } if (right_type->is_nil_type()) { + right = Expression::make_nil(location); if (left_type->array_type() != NULL && left_type->array_type()->length() == NULL) { Array_type* at = left_type->array_type(); - left_expr = at->get_value_pointer(context->gogo(), left_expr); - left_tree = left_expr->get_tree(context); - right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node); + left = at->get_value_pointer(context->gogo(), left); } else if (left_type->interface_type() != NULL) { // An interface is nil if the first field is nil. - tree left_type_tree = TREE_TYPE(left_tree); - go_assert(TREE_CODE(left_type_tree) == RECORD_TYPE); - tree field = TYPE_FIELDS(left_type_tree); - left_tree = build3(COMPONENT_REF, TREE_TYPE(field), left_tree, - field, NULL_TREE); - right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node); - } - else - { - go_assert(POINTER_TYPE_P(TREE_TYPE(left_tree))); - right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node); + left = Expression::make_field_reference(left, 0, location); } } + tree left_tree = left->get_tree(context); + tree right_tree = right->get_tree(context); if (left_tree == error_mark_node || right_tree == error_mark_node) return error_mark_node; @@ -9745,21 +9724,13 @@ Call_expression::do_must_eval_in_order() // Get the function and the first argument to use when calling an // interface method. -tree +Expression* Call_expression::interface_method_function( - Translate_context* context, Interface_field_reference_expression* interface_method, - tree* first_arg_ptr) + Expression** first_arg_ptr) { - tree expr = interface_method->expr()->get_tree(context); - if (expr == error_mark_node) - return error_mark_node; - expr = save_expr(expr); - tree first_arg = interface_method->get_underlying_object_tree(context, expr); - if (first_arg == error_mark_node) - return error_mark_node; - *first_arg_ptr = first_arg; - return interface_method->get_function_tree(context, expr); + *first_arg_ptr = interface_method->get_underlying_object(); + return interface_method->get_function(); } // Build the call expression. @@ -9889,8 +9860,12 @@ Call_expression::do_get_tree(Translate_c } else { - fn = this->interface_method_function(context, interface_method, - &args[0]); + Expression* first_arg; + Expression* fn_expr = + this->interface_method_function(interface_method, &first_arg); + args[0] = first_arg->get_tree(context); + fn = fn_expr->get_tree(context); + if (fn == error_mark_node) return error_mark_node; closure_tree = NULL_TREE; @@ -11623,58 +11598,39 @@ Expression::make_field_reference(Express // Class Interface_field_reference_expression. -// Return a tree for the pointer to the function to call. +// Return an expression for the pointer to the function to call. -tree -Interface_field_reference_expression::get_function_tree(Translate_context*, - tree expr) +Expression* +Interface_field_reference_expression::get_function() { - if (this->expr_->type()->points_to() != NULL) - expr = build_fold_indirect_ref(expr); - - tree expr_type = TREE_TYPE(expr); - go_assert(TREE_CODE(expr_type) == RECORD_TYPE); - - tree field = TYPE_FIELDS(expr_type); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__methods") == 0); - - tree table = build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE); - go_assert(POINTER_TYPE_P(TREE_TYPE(table))); + Expression* ref = this->expr_; + Location loc = this->location(); + if (ref->type()->points_to() != NULL) + ref = Expression::make_unary(OPERATOR_MULT, ref, loc); - table = build_fold_indirect_ref(table); - go_assert(TREE_CODE(TREE_TYPE(table)) == RECORD_TYPE); + Expression* mtable = + Expression::make_interface_info(ref, INTERFACE_INFO_METHODS, loc); + Struct_type* mtable_type = mtable->type()->points_to()->struct_type(); std::string name = Gogo::unpack_hidden_name(this->name_); - for (field = DECL_CHAIN(TYPE_FIELDS(TREE_TYPE(table))); - field != NULL_TREE; - field = DECL_CHAIN(field)) - { - if (name == IDENTIFIER_POINTER(DECL_NAME(field))) - break; - } - go_assert(field != NULL_TREE); - - return build3(COMPONENT_REF, TREE_TYPE(field), table, field, NULL_TREE); + unsigned int index; + const Struct_field* field = mtable_type->find_local_field(name, &index); + go_assert(field != NULL); + mtable = Expression::make_unary(OPERATOR_MULT, mtable, loc); + return Expression::make_field_reference(mtable, index, loc); } -// Return a tree for the first argument to pass to the interface +// Return an expression for the first argument to pass to the interface // function. -tree -Interface_field_reference_expression::get_underlying_object_tree( - Translate_context*, - tree expr) +Expression* +Interface_field_reference_expression::get_underlying_object() { - if (this->expr_->type()->points_to() != NULL) - expr = build_fold_indirect_ref(expr); - - tree expr_type = TREE_TYPE(expr); - go_assert(TREE_CODE(expr_type) == RECORD_TYPE); - - tree field = DECL_CHAIN(TYPE_FIELDS(expr_type)); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0); - - return build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE); + Expression* expr = this->expr_; + if (expr->type()->points_to() != NULL) + expr = Expression::make_unary(OPERATOR_MULT, expr, this->location()); + return Expression::make_interface_info(expr, INTERFACE_INFO_OBJECT, + this->location()); } // Traversal. @@ -11694,9 +11650,7 @@ Interface_field_reference_expression::do Statement_inserter* inserter, int) { - if (this->expr_->var_expression() == NULL - && this->expr_->temporary_reference_expression() == NULL - && this->expr_->set_and_use_temporary_expression() == NULL) + if (!this->expr_->is_variable()) { Temporary_statement* temp = Statement::make_temporary(this->expr_->type(), NULL, this->location()); @@ -11923,30 +11877,22 @@ Interface_field_reference_expression::do Expression* expr = Expression::make_struct_composite_literal(st, vals, loc); expr = Expression::make_heap_composite(expr, loc); - tree closure_tree = expr->get_tree(context); + Bexpression* bclosure = tree_to_expr(expr->get_tree(context)); + Expression* nil_check = + Expression::make_binary(OPERATOR_EQEQ, this->expr_, + Expression::make_nil(loc), loc); + Bexpression* bnil_check = tree_to_expr(nil_check->get_tree(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 nil_check_tree = Expression::comparison_tree(context, - Type::lookup_bool_type(), - OPERATOR_EQEQ, - this->expr_, - Expression::make_nil(loc), - loc); - Expression* crash_expr = - context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc); - tree crash = crash_expr->get_tree(context); - if (closure_tree == error_mark_node - || nil_check_tree == error_mark_node - || crash == error_mark_node) - return error_mark_node; - return fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR, - TREE_TYPE(closure_tree), - build3_loc(loc.gcc_location(), COND_EXPR, - void_type_node, nil_check_tree, crash, - NULL_TREE), - closure_tree); + Gogo* gogo = context->gogo(); + Expression* crash = gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc); + Bexpression* bcrash = tree_to_expr(crash->get_tree(context)); + + Bexpression* bcond = + gogo->backend()->conditional_expression(bnil_check, bcrash, NULL, loc); + Bstatement* cond_statement = gogo->backend()->expression_statement(bcond); + Bexpression* ret = + gogo->backend()->compound_expression(cond_statement, bclosure, loc); + return expr_to_tree(ret); } // Dump ast representation for an interface field reference. @@ -14754,6 +14700,155 @@ Expression::make_slice_info(Expression* return new Slice_info_expression(slice, slice_info, location); } + +// An expression that evaluates to some characteristic of a non-empty interface. +// This is used to access the method table or underlying object of an interface. + +class Interface_info_expression : public Expression +{ + public: + Interface_info_expression(Expression* iface, Interface_info iface_info, + Location location) + : Expression(EXPRESSION_INTERFACE_INFO, location), + iface_(iface), iface_info_(iface_info) + { } + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return new Interface_info_expression(this->iface_->copy(), + this->iface_info_, this->location()); + } + + tree + do_get_tree(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + void + do_issue_nil_check() + { this->iface_->issue_nil_check(); } + + private: + // The interface for which we are getting information. + Expression* iface_; + // What information we want. + Interface_info iface_info_; +}; + +// Return the type of the interface info. + +Type* +Interface_info_expression::do_type() +{ + switch (this->iface_info_) + { + case INTERFACE_INFO_METHODS: + { + Location loc = this->location(); + Struct_field_list* sfl = new Struct_field_list(); + Type* pdt = Type::make_type_descriptor_ptr_type(); + sfl->push_back( + Struct_field(Typed_identifier("__type_descriptor", pdt, loc))); + + Interface_type* itype = this->iface_->type()->interface_type(); + for (Typed_identifier_list::const_iterator p = itype->methods()->begin(); + p != itype->methods()->end(); + ++p) + { + Function_type* ft = p->type()->function_type(); + go_assert(ft->receiver() == NULL); + + const Typed_identifier_list* params = ft->parameters(); + Typed_identifier_list* mparams = new Typed_identifier_list(); + if (params != NULL) + mparams->reserve(params->size() + 1); + Type* vt = Type::make_pointer_type(Type::make_void_type()); + mparams->push_back(Typed_identifier("", vt, ft->location())); + if (params != NULL) + { + for (Typed_identifier_list::const_iterator pp = params->begin(); + pp != params->end(); + ++pp) + mparams->push_back(*pp); + } + + Typed_identifier_list* mresults = (ft->results() == NULL + ? NULL + : ft->results()->copy()); + Backend_function_type* mft = + Type::make_backend_function_type(NULL, mparams, mresults, + ft->location()); + + std::string fname = Gogo::unpack_hidden_name(p->name()); + sfl->push_back(Struct_field(Typed_identifier(fname, mft, loc))); + } + + return Type::make_pointer_type(Type::make_struct_type(sfl, loc)); + } + case INTERFACE_INFO_OBJECT: + return Type::make_pointer_type(Type::make_void_type()); + default: + go_unreachable(); + } +} + +// Return interface information in GENERIC. + +tree +Interface_info_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + + Bexpression* biface = tree_to_expr(this->iface_->get_tree(context)); + Bexpression* ret; + switch (this->iface_info_) + { + case INTERFACE_INFO_METHODS: + case INTERFACE_INFO_OBJECT: + ret = gogo->backend()->struct_field_expression(biface, this->iface_info_, + this->location()); + break; + default: + go_unreachable(); + } + return expr_to_tree(ret); +} + +// Dump ast representation for an interface info expression. + +void +Interface_info_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "interfaceinfo("; + this->iface_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << ","; + ast_dump_context->ostream() << + (this->iface_info_ == INTERFACE_INFO_METHODS ? "methods" + : this->iface_info_ == INTERFACE_INFO_OBJECT ? "object" + : "unknown"); + ast_dump_context->ostream() << ")"; +} + +// Make an interface info expression. + +Expression* +Expression::make_interface_info(Expression* iface, Interface_info iface_info, + Location location) +{ + return new Interface_info_expression(iface, iface_info, location); +} + // An expression which evaluates to the offset of a field within a // struct. This, like Type_info_expression, q.v., is only used to // initialize fields of a type descriptor. Index: gcc/go/gofrontend/expressions.h =================================================================== --- gcc/go/gofrontend/expressions.h (revision 206509) +++ gcc/go/gofrontend/expressions.h (working copy) @@ -103,6 +103,7 @@ class Expression EXPRESSION_TYPE_DESCRIPTOR, EXPRESSION_TYPE_INFO, EXPRESSION_SLICE_INFO, + EXPRESSION_INTERFACE_INFO, EXPRESSION_STRUCT_FIELD_OFFSET, EXPRESSION_MAP_DESCRIPTOR, EXPRESSION_LABEL_ADDR @@ -356,6 +357,21 @@ class Expression static Expression* make_slice_info(Expression* slice, Slice_info, Location); + + // Make an expression that evaluates to some characteristic of a + // interface. For simplicity, the enum values must match the field indexes + // of a non-empty interface in the underlying struct. + enum Interface_info + { + // The methods of an interface. + INTERFACE_INFO_METHODS, + // The first argument to pass to an interface method. + INTERFACE_INFO_OBJECT + }; + + static Expression* + make_interface_info(Expression* iface, Interface_info, Location); + // Make an expression which evaluates to the offset of a field in a // struct. This is only used for type descriptors, so there is no // location parameter. @@ -1508,10 +1524,9 @@ class Call_expression : public Expressio bool check_argument_type(int, const Type*, const Type*, Location, bool); - tree - interface_method_function(Translate_context*, - Interface_field_reference_expression*, - tree*); + Expression* + interface_method_function(Interface_field_reference_expression*, + Expression**); tree set_results(Translate_context*, tree); @@ -2115,16 +2130,14 @@ class Interface_field_reference_expressi static Named_object* create_thunk(Gogo*, Interface_type* type, const std::string& name); - // Return a tree for the pointer to the function to call, given a - // tree for the expression. - tree - get_function_tree(Translate_context*, tree); + // Return an expression for the pointer to the function to call. + Expression* + get_function(); - // Return a tree for the first argument to pass to the interface - // function, given a tree for the expression. This is the real - // object associated with the interface object. - tree - get_underlying_object_tree(Translate_context*, tree); + // Return an expression for the first argument to pass to the interface + // function. This is the real object associated with the interface object. + Expression* + get_underlying_object(); protected: int Index: gcc/go/gofrontend/backend.h =================================================================== --- gcc/go/gofrontend/backend.h (revision 206474) +++ gcc/go/gofrontend/backend.h (working copy) @@ -284,6 +284,16 @@ class Backend virtual Bexpression* struct_field_expression(Bexpression* bstruct, size_t index, Location) = 0; + // Create an expression that executes BSTAT before BEXPR. + virtual Bexpression* + compound_expression(Bstatement* bstat, Bexpression* bexpr, Location) = 0; + + // Return an expression that executes THEN_EXPR if CONDITION is true, or + // ELSE_EXPR otherwise. ELSE_EXPR may be NULL. + virtual Bexpression* + conditional_expression(Bexpression* condition, Bexpression* then_expr, + Bexpression* else_expr, Location) = 0; + // Statements. // Create an error statement. This is used for cases which should Index: gcc/go/go-gcc.cc =================================================================== --- gcc/go/go-gcc.cc (revision 206474) +++ gcc/go/go-gcc.cc (working copy) @@ -246,6 +246,12 @@ class Gcc_backend : public Backend Bexpression* struct_field_expression(Bexpression*, size_t, Location); + Bexpression* + compound_expression(Bstatement*, Bexpression*, Location); + + Bexpression* + conditional_expression(Bexpression*, Bexpression*, Bexpression*, Location); + // Statements. Bstatement* @@ -1034,6 +1040,41 @@ Gcc_backend::struct_field_expression(Bex return tree_to_expr(ret); } +// Return an expression that executes BSTAT before BEXPR. + +Bexpression* +Gcc_backend::compound_expression(Bstatement* bstat, Bexpression* bexpr, + Location location) +{ + tree stat = bstat->get_tree(); + tree expr = bexpr->get_tree(); + if (stat == error_mark_node || expr == error_mark_node) + return this->error_expression(); + tree ret = fold_build2_loc(location.gcc_location(), COMPOUND_EXPR, + TREE_TYPE(expr), stat, expr); + return this->make_expression(ret); +} + +// Return an expression that executes THEN_EXPR if CONDITION is true, or +// ELSE_EXPR otherwise. + +Bexpression* +Gcc_backend::conditional_expression(Bexpression* condition, + Bexpression* then_expr, + Bexpression* else_expr, Location location) +{ + tree cond_tree = condition->get_tree(); + tree then_tree = then_expr->get_tree(); + tree else_tree = else_expr == NULL ? NULL_TREE : else_expr->get_tree(); + if (cond_tree == error_mark_node + || then_tree == error_mark_node + || else_tree == error_mark_node) + return this->error_expression(); + tree ret = build3_loc(location.gcc_location(), COND_EXPR, void_type_node, + cond_tree, then_tree, else_tree); + return this->make_expression(ret); +} + // An expression as a statement. Bstatement*