This patch from Chris Manghane changes gccgo to use the backend interface for slice info. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline.
Ian
diff -r 44fc257ad7f2 go/expressions.cc --- a/go/expressions.cc Thu Jan 09 15:25:44 2014 -0800 +++ b/go/expressions.cc Thu Jan 09 22:31:00 2014 -0800 @@ -3060,6 +3060,9 @@ Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + bool do_is_constant() const; @@ -3203,6 +3206,25 @@ return this; } +// Flatten a type conversion by using a temporary variable for the slice +// in slice to string conversions. + +Expression* +Type_conversion_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + if (this->type()->is_string_type() + && this->expr_->type()->is_slice_type() + && !this->expr_->is_variable()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, this->expr_, this->location()); + inserter->insert(temp); + this->expr_ = Expression::make_temporary_reference(temp, this->location()); + } + return this; +} + // Return whether a type conversion is a constant. bool @@ -3361,47 +3383,24 @@ } else if (type->is_string_type() && expr_type->is_slice_type()) { - if (!DECL_P(expr_tree)) - expr_tree = save_expr(expr_tree); - - Type* int_type = Type::lookup_integer_type("int"); - tree int_type_tree = type_to_tree(int_type->get_backend(gogo)); - + Location location = this->location(); Array_type* a = expr_type->array_type(); Type* e = a->element_type()->forwarded(); go_assert(e->integer_type() != NULL); - tree valptr = fold_convert(const_ptr_type_node, - a->value_pointer_tree(gogo, expr_tree)); - tree len = a->length_tree(gogo, expr_tree); - len = fold_convert_loc(this->location().gcc_location(), int_type_tree, - len); + go_assert(this->expr_->is_variable()); + + Runtime::Function code; if (e->integer_type()->is_byte()) - { - static tree byte_array_to_string_fndecl; - ret = Gogo::call_builtin(&byte_array_to_string_fndecl, - this->location(), - "__go_byte_array_to_string", - 2, - type_tree, - const_ptr_type_node, - valptr, - int_type_tree, - len); - } + code = Runtime::BYTE_ARRAY_TO_STRING; else - { - go_assert(e->integer_type()->is_rune()); - static tree int_array_to_string_fndecl; - ret = Gogo::call_builtin(&int_array_to_string_fndecl, - this->location(), - "__go_int_array_to_string", - 2, - type_tree, - const_ptr_type_node, - valptr, - int_type_tree, - len); - } + { + go_assert(e->integer_type()->is_rune()); + code = Runtime::INT_ARRAY_TO_STRING; + } + Expression* valptr = a->get_value_pointer(gogo, this->expr_); + Expression* len = a->get_length(gogo, this->expr_); + Expression* a2s_expr = Runtime::make_call(code, location, 2, valptr, len); + ret = a2s_expr->get_tree(context); } else if (type->is_slice_type() && expr_type->is_string_type()) { @@ -6595,6 +6594,7 @@ { std::swap(left_type, right_type); std::swap(left_tree, right_tree); + std::swap(left_expr, right_expr); } if (right_type->is_nil_type()) @@ -6603,7 +6603,8 @@ && left_type->array_type()->length() == NULL) { Array_type* at = left_type->array_type(); - left_tree = at->value_pointer_tree(context->gogo(), left_tree); + 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); } else if (left_type->interface_type() != NULL) @@ -7037,6 +7038,9 @@ Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + bool do_is_constant() const; @@ -7367,6 +7371,36 @@ return this; } +// Flatten a builtin call expression. This turns the arguments of copy and +// append into temporary expressions. + +Expression* +Builtin_call_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + if (this->code_ == BUILTIN_APPEND + || this->code_ == BUILTIN_COPY) + { + Location loc = this->location(); + Type* at = this->args()->front()->type(); + for (Expression_list::iterator pa = this->args()->begin(); + pa != this->args()->end(); + ++pa) + { + if ((*pa)->is_nil_expression()) + *pa = Expression::make_slice_composite_literal(at, NULL, loc); + if (!(*pa)->is_variable()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + } + return this; +} + // Lower a make expression. Expression* @@ -8503,7 +8537,8 @@ return error_mark_node; } this->seen_ = true; - val_tree = arg_type->array_type()->length_tree(gogo, arg_tree); + Expression* len = arg_type->array_type()->get_length(gogo, arg); + val_tree = len->get_tree(context); this->seen_ = false; } else if (arg_type->map_type() != NULL) @@ -8543,8 +8578,9 @@ return error_mark_node; } this->seen_ = true; - val_tree = arg_type->array_type()->capacity_tree(gogo, - arg_tree); + Expression* cap = + arg_type->array_type()->get_capacity(gogo, arg); + val_tree = cap->get_tree(context); this->seen_ = false; } else if (arg_type->channel_type() != NULL) @@ -8848,9 +8884,11 @@ Type* arg1_type = arg1->type(); Array_type* at = arg1_type->array_type(); - arg1_tree = save_expr(arg1_tree); - tree arg1_val = at->value_pointer_tree(gogo, arg1_tree); - tree arg1_len = at->length_tree(gogo, arg1_tree); + go_assert(arg1->is_variable()); + Expression* arg1_valptr = at->get_value_pointer(gogo, arg1); + Expression* arg1_len_expr = at->get_length(gogo, arg1); + tree arg1_val = arg1_valptr->get_tree(context); + tree arg1_len = arg1_len_expr->get_tree(context); if (arg1_val == error_mark_node || arg1_len == error_mark_node) return error_mark_node; @@ -8860,9 +8898,11 @@ if (arg2_type->is_slice_type()) { at = arg2_type->array_type(); - arg2_tree = save_expr(arg2_tree); - arg2_val = at->value_pointer_tree(gogo, arg2_tree); - arg2_len = at->length_tree(gogo, arg2_tree); + go_assert(arg2->is_variable()); + Expression* arg2_valptr = at->get_value_pointer(gogo, arg2); + Expression* arg2_len_expr = at->get_length(gogo, arg2); + arg2_val = arg2_valptr->get_tree(context); + arg2_len = arg2_len_expr->get_tree(context); } else { @@ -8950,23 +8990,15 @@ } else { - arg2_tree = Expression::convert_for_assignment(context, at, - arg2->type(), - arg2_tree, - location); - if (arg2_tree == error_mark_node) + go_assert(arg2->is_variable()); + arg2_val = + at->get_value_pointer(gogo, arg2)->get_tree(context); + arg2_len = at->get_length(gogo, arg2)->get_tree(context); + Btype* element_btype = element_type->get_backend(gogo); + tree element_type_tree = type_to_tree(element_btype); + if (element_type_tree == error_mark_node) return error_mark_node; - - arg2_tree = save_expr(arg2_tree); - - arg2_val = at->value_pointer_tree(gogo, arg2_tree); - arg2_len = at->length_tree(gogo, arg2_tree); - - Btype* element_btype = element_type->get_backend(gogo); - tree element_type_tree = type_to_tree(element_btype); - if (element_type_tree == error_mark_node) - return error_mark_node; - element_size = TYPE_SIZE_UNIT(element_type_tree); + element_size = TYPE_SIZE_UNIT(element_type_tree); } arg2_val = fold_convert_loc(location.gcc_location(), ptr_type_node, @@ -10371,6 +10403,9 @@ do_check_types(Gogo*); Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Expression* do_copy() { return Expression::make_array_index(this->array_->copy(), @@ -10611,6 +10646,22 @@ } } +// Flatten array indexing by using a temporary variable for slices. + +Expression* +Array_index_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + Location loc = this->location(); + if (this->array_->type()->is_slice_type() && !this->array_->is_variable()) + { + Temporary_statement* temp = Statement::make_temporary(NULL, this->array_, loc); + inserter->insert(temp); + this->array_ = Expression::make_temporary_reference(temp, loc); + } + return this; +} + // Return whether this expression is addressable. bool @@ -10643,22 +10694,17 @@ go_assert(this->array_->type()->is_error()); return error_mark_node; } + go_assert(!array_type->is_slice_type() || this->array_->is_variable()); tree type_tree = type_to_tree(array_type->get_backend(gogo)); if (type_tree == error_mark_node) return error_mark_node; - tree array_tree = this->array_->get_tree(context); - if (array_tree == error_mark_node) - return error_mark_node; - - if (array_type->length() == NULL && !DECL_P(array_tree)) - array_tree = save_expr(array_tree); - tree length_tree = NULL_TREE; if (this->end_ == NULL || this->end_->is_nil_expression()) { - length_tree = array_type->length_tree(gogo, array_tree); + Expression* len = array_type->get_length(gogo, this->array_); + length_tree = len->get_tree(context); if (length_tree == error_mark_node) return error_mark_node; length_tree = save_expr(length_tree); @@ -10667,7 +10713,8 @@ tree capacity_tree = NULL_TREE; if (this->end_ != NULL) { - capacity_tree = array_type->capacity_tree(gogo, array_tree); + Expression* cap = array_type->get_capacity(gogo, this->array_); + capacity_tree = cap->get_tree(context); if (capacity_tree == error_mark_node) return error_mark_node; capacity_tree = save_expr(capacity_tree); @@ -10732,13 +10779,18 @@ if (array_type->length() != NULL) { // Fixed array. + tree array_tree = this->array_->get_tree(context); + if (array_tree == error_mark_node) + return error_mark_node; return build4(ARRAY_REF, TREE_TYPE(type_tree), array_tree, start_tree, NULL_TREE, NULL_TREE); } else { // Open array. - tree values = array_type->value_pointer_tree(gogo, array_tree); + Expression* valptr = + array_type->get_value_pointer(gogo, this->array_); + tree values = valptr->get_tree(context); Type* element_type = array_type->element_type(); Btype* belement_type = element_type->get_backend(gogo); tree element_type_tree = type_to_tree(belement_type); @@ -10820,7 +10872,8 @@ start_tree), element_size); - tree value_pointer = array_type->value_pointer_tree(gogo, array_tree); + Expression* valptr = array_type->get_value_pointer(gogo, this->array_); + tree value_pointer = valptr->get_tree(context); if (value_pointer == error_mark_node) return error_mark_node; @@ -14133,6 +14186,22 @@ } } +// Return true if this is a variable or temporary_variable. + +bool +Expression::is_variable() const +{ + switch (this->classification_) + { + case EXPRESSION_VAR_REFERENCE: + case EXPRESSION_TEMPORARY_REFERENCE: + case EXPRESSION_SET_AND_USE_TEMPORARY: + return true; + default: + return false; + } +} + // Return true if this is a reference to a local variable. bool @@ -14574,6 +14643,117 @@ return new Type_info_expression(type, type_info); } +// An expression that evaluates to some characteristic of a slice. +// This is used when indexing, bound-checking, or nil checking a slice. + +class Slice_info_expression : public Expression +{ + public: + Slice_info_expression(Expression* slice, Slice_info slice_info, + Location location) + : Expression(EXPRESSION_SLICE_INFO, location), + slice_(slice), slice_info_(slice_info) + { } + + protected: + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy() + { + return new Slice_info_expression(this->slice_->copy(), this->slice_info_, + this->location()); + } + + tree + do_get_tree(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + void + do_issue_nil_check() + { this->slice_->issue_nil_check(); } + + private: + // The slice for which we are getting information. + Expression* slice_; + // What information we want. + Slice_info slice_info_; +}; + +// Return the type of the slice info. + +Type* +Slice_info_expression::do_type() +{ + switch (this->slice_info_) + { + case SLICE_INFO_VALUE_POINTER: + return Type::make_pointer_type( + this->slice_->type()->array_type()->element_type()); + case SLICE_INFO_LENGTH: + case SLICE_INFO_CAPACITY: + return Type::lookup_integer_type("int"); + default: + go_unreachable(); + } +} + +// Return slice information in GENERIC. + +tree +Slice_info_expression::do_get_tree(Translate_context* context) +{ + Gogo* gogo = context->gogo(); + + Bexpression* bslice = tree_to_expr(this->slice_->get_tree(context)); + Bexpression* ret; + switch (this->slice_info_) + { + case SLICE_INFO_VALUE_POINTER: + case SLICE_INFO_LENGTH: + case SLICE_INFO_CAPACITY: + ret = gogo->backend()->struct_field_expression(bslice, this->slice_info_, + this->location()); + break; + default: + go_unreachable(); + } + return expr_to_tree(ret); +} + +// Dump ast representation for a type info expression. + +void +Slice_info_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + ast_dump_context->ostream() << "sliceinfo("; + this->slice_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << ","; + ast_dump_context->ostream() << + (this->slice_info_ == SLICE_INFO_VALUE_POINTER ? "values" + : this->slice_info_ == SLICE_INFO_LENGTH ? "length" + : this->slice_info_ == SLICE_INFO_CAPACITY ? "capacity " + : "unknown"); + ast_dump_context->ostream() << ")"; +} + +// Make a slice info expression. + +Expression* +Expression::make_slice_info(Expression* slice, Slice_info slice_info, + Location location) +{ + return new Slice_info_expression(slice, slice_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. diff -r 44fc257ad7f2 go/expressions.h --- a/go/expressions.h Thu Jan 09 15:25:44 2014 -0800 +++ b/go/expressions.h Thu Jan 09 22:31:00 2014 -0800 @@ -102,6 +102,7 @@ EXPRESSION_RECEIVE, EXPRESSION_TYPE_DESCRIPTOR, EXPRESSION_TYPE_INFO, + EXPRESSION_SLICE_INFO, EXPRESSION_STRUCT_FIELD_OFFSET, EXPRESSION_MAP_DESCRIPTOR, EXPRESSION_LABEL_ADDR @@ -339,6 +340,22 @@ static Expression* make_type_info(Type* type, Type_info); + // Make an expression that evaluates to some characteristic of a + // slice. For simplicity, the enum values must match the field indexes + // in the underlying struct. + enum Slice_info + { + // The underlying data of the slice. + SLICE_INFO_VALUE_POINTER, + // The length of the slice. + SLICE_INFO_LENGTH, + // The capacity of the slice. + SLICE_INFO_CAPACITY + }; + + static Expression* + make_slice_info(Expression* slice, Slice_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. @@ -544,6 +561,10 @@ bool is_nonconstant_composite_literal() const; + // Return true if this is a variable or temporary variable. + bool + is_variable() const; + // Return true if this is a reference to a local variable. bool is_local_variable() const; diff -r 44fc257ad7f2 go/gogo.cc --- a/go/gogo.cc Thu Jan 09 15:25:44 2014 -0800 +++ b/go/gogo.cc Thu Jan 09 22:31:00 2014 -0800 @@ -2846,6 +2846,15 @@ return TRAVERSE_SKIP_COMPONENTS; } +// Flatten a block. + +void +Gogo::flatten_block(Named_object* function, Block* block) +{ + Flatten flatten(this, function); + block->traverse(&flatten); +} + // Flatten an expression. INSERTER may be NULL, in which case the // expression had better not need to create any temporaries. diff -r 44fc257ad7f2 go/gogo.h --- a/go/gogo.h Thu Jan 09 15:25:44 2014 -0800 +++ b/go/gogo.h Thu Jan 09 22:31:00 2014 -0800 @@ -487,6 +487,10 @@ void lower_constant(Named_object*); + // Flatten all the statements in a block. + void + flatten_block(Named_object* function, Block*); + // Flatten an expression. void flatten_expression(Named_object* function, Statement_inserter*, Expression**); diff -r 44fc257ad7f2 go/statements.cc --- a/go/statements.cc Thu Jan 09 15:25:44 2014 -0800 +++ b/go/statements.cc Thu Jan 09 22:31:00 2014 -0800 @@ -2471,6 +2471,7 @@ gogo->add_block(b, location); gogo->lower_block(function, b); + gogo->flatten_block(function, b); // We already ran the determine_types pass, so we need to run it // just for the call statement now. The other types are known. diff -r 44fc257ad7f2 go/types.cc --- a/go/types.cc Thu Jan 09 15:25:44 2014 -0800 +++ b/go/types.cc Thu Jan 09 22:31:00 2014 -0800 @@ -6000,84 +6000,53 @@ } } -// Return a tree for a pointer to the values in ARRAY. - -tree -Array_type::value_pointer_tree(Gogo*, tree array) const -{ - tree ret; +// Return an expression for a pointer to the values in ARRAY. + +Expression* +Array_type::get_value_pointer(Gogo*, Expression* array) const +{ if (this->length() != NULL) { // Fixed array. - ret = fold_convert(build_pointer_type(TREE_TYPE(TREE_TYPE(array))), - build_fold_addr_expr(array)); - } - else - { - // Open array. - tree field = TYPE_FIELDS(TREE_TYPE(array)); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), - "__values") == 0); - ret = fold_build3(COMPONENT_REF, TREE_TYPE(field), array, field, - NULL_TREE); - } - if (TREE_CONSTANT(array)) - TREE_CONSTANT(ret) = 1; - return ret; -} - -// Return a tree for the length of the array ARRAY which has this + go_assert(array->type()->array_type() != NULL); + Type* etype = array->type()->array_type()->element_type(); + array = Expression::make_unary(OPERATOR_AND, array, array->location()); + return Expression::make_cast(Type::make_pointer_type(etype), array, + array->location()); + } + + // Open array. + return Expression::make_slice_info(array, + Expression::SLICE_INFO_VALUE_POINTER, + array->location()); +} + +// Return an expression for the length of the array ARRAY which has this // type. -tree -Array_type::length_tree(Gogo* gogo, tree array) +Expression* +Array_type::get_length(Gogo*, Expression* array) const { if (this->length_ != NULL) - { - if (TREE_CODE(array) == SAVE_EXPR) - return this->get_length_tree(gogo); - else - { - tree len = this->get_length_tree(gogo); - return omit_one_operand(TREE_TYPE(len), len, array); - } - } + return this->length_; // This is an open array. We need to read the length field. - - tree type = TREE_TYPE(array); - go_assert(TREE_CODE(type) == RECORD_TYPE); - - tree field = DECL_CHAIN(TYPE_FIELDS(type)); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0); - - tree ret = build3(COMPONENT_REF, TREE_TYPE(field), array, field, NULL_TREE); - if (TREE_CONSTANT(array)) - TREE_CONSTANT(ret) = 1; - return ret; -} - -// Return a tree for the capacity of the array ARRAY which has this + return Expression::make_slice_info(array, Expression::SLICE_INFO_LENGTH, + array->location()); +} + +// Return an expression for the capacity of the array ARRAY which has this // type. -tree -Array_type::capacity_tree(Gogo* gogo, tree array) +Expression* +Array_type::get_capacity(Gogo*, Expression* array) const { if (this->length_ != NULL) - { - tree len = this->get_length_tree(gogo); - return omit_one_operand(TREE_TYPE(len), len, array); - } + return this->length_; // This is an open array. We need to read the capacity field. - - tree type = TREE_TYPE(array); - go_assert(TREE_CODE(type) == RECORD_TYPE); - - tree field = DECL_CHAIN(DECL_CHAIN(TYPE_FIELDS(type))); - go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0); - - return build3(COMPONENT_REF, TREE_TYPE(field), array, field, NULL_TREE); + return Expression::make_slice_info(array, Expression::SLICE_INFO_CAPACITY, + array->location()); } // Export. diff -r 44fc257ad7f2 go/types.h --- a/go/types.h Thu Jan 09 15:25:44 2014 -0800 +++ b/go/types.h Thu Jan 09 22:31:00 2014 -0800 @@ -2312,17 +2312,17 @@ array_has_hidden_fields(const Named_type* within, std::string* reason) const { return this->element_type_->has_hidden_fields(within, reason); } - // Return a tree for the pointer to the values in an array. - tree - value_pointer_tree(Gogo*, tree array) const; - - // Return a tree for the length of an array with this type. - tree - length_tree(Gogo*, tree array); - - // Return a tree for the capacity of an array with this type. - tree - capacity_tree(Gogo*, tree array); + // Return an expression for the pointer to the values in an array. + Expression* + get_value_pointer(Gogo*, Expression* array) const; + + // Return an expression for the length of an array with this type. + Expression* + get_length(Gogo*, Expression* array) const; + + // Return an expression for the capacity of an array with this type. + Expression* + get_capacity(Gogo*, Expression* array) const; // Import an array type. static Array_type*