@@ -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.
@@ -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;
@@ -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.
@@ -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**);
@@ -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.
@@ -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.
@@ -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*