diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/go/gofrontend/MERGE | 2 | ||||
-rw-r--r-- | gcc/go/gofrontend/escape.cc | 63 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 534 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.cc | 4 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.def | 24 |
5 files changed, 407 insertions, 220 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index e2fb0db..4d04705 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -5ddcdfb0b2bb992a70b391ab34bf15291a514e48 +fe38baff61b9b9426a4f60ff078cf3c8722bf94d The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc index ba55ea3..bcea63d 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -284,20 +284,19 @@ Node::op_format() const op << "panic"; break; - case Runtime::APPEND: + case Runtime::GROWSLICE: op << "append"; break; - case Runtime::COPY: + case Runtime::SLICECOPY: + case Runtime::SLICESTRINGCOPY: + case Runtime::TYPEDSLICECOPY: op << "copy"; break; case Runtime::MAKECHAN: case Runtime::MAKEMAP: - case Runtime::MAKESLICE1: - case Runtime::MAKESLICE2: - case Runtime::MAKESLICE1BIG: - case Runtime::MAKESLICE2BIG: + case Runtime::MAKESLICE: op << "make"; break; @@ -419,10 +418,7 @@ Node::is_big(Escape_context* context) const Func_expression* fn = call->fn()->func_expression(); if (fn != NULL && fn->is_runtime_function() - && (fn->runtime_code() == Runtime::MAKESLICE1 - || fn->runtime_code() == Runtime::MAKESLICE2 - || fn->runtime_code() == Runtime::MAKESLICE1BIG - || fn->runtime_code() == Runtime::MAKESLICE2BIG)) + && fn->runtime_code() == Runtime::MAKESLICE) { // Second argument is length. Expression_list::iterator p = call->args()->begin(); @@ -1201,13 +1197,25 @@ Escape_analysis_assign::expression(Expression** pexpr) } break; - case Runtime::APPEND: + case Runtime::GROWSLICE: { - // Unlike gc/esc.go, a call to append has already had its - // varargs lowered into a slice of arguments. - // The content of the appended slice leaks. - Node* appended = Node::make_node(call->args()->back()); - this->assign_deref(this->context_->sink(), appended); + // The contents being appended leak. + if (call->is_varargs()) + { + Node* appended = Node::make_node(call->args()->back()); + this->assign_deref(this->context_->sink(), appended); + } + else + { + for (Expression_list::const_iterator pa = + call->args()->begin(); + pa != call->args()->end(); + ++pa) + { + Node* arg = Node::make_node(*pa); + this->assign(this->context_->sink(), arg); + } + } if (debug_level > 2) go_error_at((*pexpr)->location(), @@ -1219,7 +1227,9 @@ Escape_analysis_assign::expression(Expression** pexpr) } break; - case Runtime::COPY: + case Runtime::SLICECOPY: + case Runtime::SLICESTRINGCOPY: + case Runtime::TYPEDSLICECOPY: { // Lose track of the copied content. Node* copied = Node::make_node(call->args()->back()); @@ -1229,10 +1239,7 @@ Escape_analysis_assign::expression(Expression** pexpr) case Runtime::MAKECHAN: case Runtime::MAKEMAP: - case Runtime::MAKESLICE1: - case Runtime::MAKESLICE2: - case Runtime::MAKESLICE1BIG: - case Runtime::MAKESLICE2BIG: + case Runtime::MAKESLICE: case Runtime::SLICEBYTETOSTRING: case Runtime::SLICERUNETOSTRING: case Runtime::STRINGTOSLICEBYTE: @@ -1829,7 +1836,7 @@ Escape_analysis_assign::assign(Node* dst, Node* src) { switch (fe->runtime_code()) { - case Runtime::APPEND: + case Runtime::GROWSLICE: { // Append returns the first argument. // The subsequent arguments are already leaked because @@ -1841,10 +1848,7 @@ Escape_analysis_assign::assign(Node* dst, Node* src) case Runtime::MAKECHAN: case Runtime::MAKEMAP: - case Runtime::MAKESLICE1: - case Runtime::MAKESLICE2: - case Runtime::MAKESLICE1BIG: - case Runtime::MAKESLICE2BIG: + case Runtime::MAKESLICE: // DST = make(...). case Runtime::SLICEBYTETOSTRING: // DST = string([]byte{...}). @@ -2608,7 +2612,7 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src, { switch (func->runtime_code()) { - case Runtime::APPEND: + case Runtime::GROWSLICE: { // Propagate escape information to appendee. Expression* appendee = call->args()->front(); @@ -2618,10 +2622,7 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src, case Runtime::MAKECHAN: case Runtime::MAKEMAP: - case Runtime::MAKESLICE1: - case Runtime::MAKESLICE2: - case Runtime::MAKESLICE1BIG: - case Runtime::MAKESLICE2BIG: + case Runtime::MAKESLICE: case Runtime::SLICEBYTETOSTRING: case Runtime::SLICERUNETOSTRING: case Runtime::STRINGTOSLICEBYTE: diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 241dc36..7f9d365 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -6951,7 +6951,9 @@ class Builtin_call_expression : public Call_expression complex_type(Type*); Expression* - lower_make(); + lower_make(Statement_inserter*); + + Expression* flatten_append(Gogo*, Named_object*, Statement_inserter*); bool check_int_value(Expression*, bool is_length); @@ -7052,7 +7054,7 @@ Builtin_call_expression::do_set_recover_arg(Expression* arg) // specific expressions. We also convert to a constant if we can. Expression* -Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, +Builtin_call_expression::do_lower(Gogo*, Named_object* function, Statement_inserter* inserter, int) { if (this->is_error_expression()) @@ -7130,7 +7132,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, break; case BUILTIN_MAKE: - return this->lower_make(); + return this->lower_make(inserter); case BUILTIN_RECOVER: if (function != NULL) @@ -7144,30 +7146,6 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, } break; - case BUILTIN_APPEND: - { - // Lower the varargs. - const Expression_list* args = this->args(); - if (args == NULL || args->empty()) - return this; - Type* slice_type = args->front()->type(); - if (!slice_type->is_slice_type()) - { - if (slice_type->is_nil_type()) - go_error_at(args->front()->location(), "use of untyped nil"); - else - go_error_at(args->front()->location(), - "argument 1 must be a slice"); - this->set_is_error(); - return this; - } - Type* element_type = slice_type->array_type()->element_type(); - this->lower_varargs(gogo, function, inserter, - Type::make_array_type(element_type, NULL), - 2, SLICE_STORAGE_DOES_NOT_ESCAPE); - } - break; - case BUILTIN_DELETE: { // Lower to a runtime function call. @@ -7233,7 +7211,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, // append into temporary expressions. Expression* -Builtin_call_expression::do_flatten(Gogo*, Named_object*, +Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, Statement_inserter* inserter) { Location loc = this->location(); @@ -7244,6 +7222,8 @@ Builtin_call_expression::do_flatten(Gogo*, Named_object*, break; case BUILTIN_APPEND: + return this->flatten_append(gogo, function, inserter); + case BUILTIN_COPY: { Type* at = this->args()->front()->type(); @@ -7285,16 +7265,19 @@ Builtin_call_expression::do_flatten(Gogo*, Named_object*, case BUILTIN_LEN: case BUILTIN_CAP: - Expression_list::iterator pa = this->args()->begin(); - if (!(*pa)->is_variable() - && ((*pa)->type()->map_type() != NULL - || (*pa)->type()->channel_type() != NULL)) - { - Temporary_statement* temp = - Statement::make_temporary(NULL, *pa, loc); - inserter->insert(temp); - *pa = Expression::make_temporary_reference(temp, loc); - } + { + Expression_list::iterator pa = this->args()->begin(); + if (!(*pa)->is_variable() + && ((*pa)->type()->map_type() != NULL + || (*pa)->type()->channel_type() != NULL)) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + break; } return this; @@ -7303,7 +7286,7 @@ Builtin_call_expression::do_flatten(Gogo*, Named_object*, // Lower a make expression. Expression* -Builtin_call_expression::lower_make() +Builtin_call_expression::lower_make(Statement_inserter* inserter) { Location loc = this->location(); @@ -7340,10 +7323,6 @@ Builtin_call_expression::lower_make() return Expression::make_error(this->location()); } - bool have_big_args = false; - Type* uintptr_type = Type::lookup_integer_type("uintptr"); - int uintptr_bits = uintptr_type->integer_type()->bits(); - Type_context int_context(Type::lookup_integer_type("int"), false); ++parg; @@ -7363,9 +7342,6 @@ Builtin_call_expression::lower_make() len_arg->determine_type(&int_context); if (!this->check_int_value(len_arg, true)) return Expression::make_error(this->location()); - if (len_arg->type()->integer_type() != NULL - && len_arg->type()->integer_type()->bits() > uintptr_bits) - have_big_args = true; ++parg; } @@ -7391,9 +7367,6 @@ Builtin_call_expression::lower_make() return Expression::make_error(this->location()); } - if (cap_arg->type()->integer_type() != NULL - && cap_arg->type()->integer_type()->bits() > uintptr_bits) - have_big_args = true; ++parg; } @@ -7404,34 +7377,236 @@ Builtin_call_expression::lower_make() } Location type_loc = first_arg->location(); - Expression* type_arg = Expression::make_type_descriptor(type, type_loc); Expression* call; if (is_slice) { + Type* et = type->array_type()->element_type(); + Expression* type_arg = Expression::make_type_descriptor(et, type_loc); if (cap_arg == NULL) - call = Runtime::make_call((have_big_args - ? Runtime::MAKESLICE1BIG - : Runtime::MAKESLICE1), - loc, 2, type_arg, len_arg); - else - call = Runtime::make_call((have_big_args - ? Runtime::MAKESLICE2BIG - : Runtime::MAKESLICE2), - loc, 3, type_arg, len_arg, cap_arg); + { + Temporary_statement* temp = Statement::make_temporary(NULL, + len_arg, + loc); + inserter->insert(temp); + len_arg = Expression::make_temporary_reference(temp, loc); + cap_arg = Expression::make_temporary_reference(temp, loc); + } + call = Runtime::make_call(Runtime::MAKESLICE, loc, 3, type_arg, + len_arg, cap_arg); } else if (is_map) - call = Runtime::make_call(Runtime::MAKEMAP, loc, 4, type_arg, len_arg, - Expression::make_nil(loc), - Expression::make_nil(loc)); + { + Expression* type_arg = Expression::make_type_descriptor(type, type_loc); + call = Runtime::make_call(Runtime::MAKEMAP, loc, 4, type_arg, len_arg, + Expression::make_nil(loc), + Expression::make_nil(loc)); + } else if (is_chan) - call = Runtime::make_call(Runtime::MAKECHAN, loc, 2, type_arg, len_arg); + { + Expression* type_arg = Expression::make_type_descriptor(type, type_loc); + call = Runtime::make_call(Runtime::MAKECHAN, loc, 2, type_arg, len_arg); + } else go_unreachable(); return Expression::make_unsafe_cast(type, call, loc); } +// Flatten a call to the predeclared append function. We do this in +// the flatten phase, not the lowering phase, so that we run after +// type checking and after order_evaluations. + +Expression* +Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, + Statement_inserter* inserter) +{ + if (this->is_error_expression()) + return this; + + Location loc = this->location(); + + const Expression_list* args = this->args(); + go_assert(args != NULL && !args->empty()); + + Type* slice_type = args->front()->type(); + go_assert(slice_type->is_slice_type()); + Type* element_type = slice_type->array_type()->element_type(); + + if (args->size() == 1) + { + // append(s) evaluates to s. + return args->front(); + } + + Type* int_type = Type::lookup_integer_type("int"); + Type* uint_type = Type::lookup_integer_type("uint"); + + // Implementing + // append(s1, s2...) + // or + // append(s1, a1, a2, a3, ...) + + // s1tmp := s1 + Temporary_statement* s1tmp = Statement::make_temporary(NULL, args->front(), + loc); + inserter->insert(s1tmp); + + // l1tmp := len(s1tmp) + Named_object* lenfn = gogo->lookup_global("len"); + Expression* lenref = Expression::make_func_reference(lenfn, NULL, loc); + Expression_list* call_args = new Expression_list(); + call_args->push_back(Expression::make_temporary_reference(s1tmp, loc)); + Expression* len = Expression::make_call(lenref, call_args, false, loc); + gogo->lower_expression(function, inserter, &len); + gogo->flatten_expression(function, inserter, &len); + Temporary_statement* l1tmp = Statement::make_temporary(int_type, len, loc); + inserter->insert(l1tmp); + + Temporary_statement* s2tmp = NULL; + Temporary_statement* l2tmp = NULL; + Expression_list* add = NULL; + Expression* len2; + if (this->is_varargs()) + { + go_assert(args->size() == 2); + + // s2tmp := s2 + s2tmp = Statement::make_temporary(NULL, args->back(), loc); + inserter->insert(s2tmp); + + // l2tmp := len(s2tmp) + lenref = Expression::make_func_reference(lenfn, NULL, loc); + call_args = new Expression_list(); + call_args->push_back(Expression::make_temporary_reference(s2tmp, loc)); + len = Expression::make_call(lenref, call_args, false, loc); + gogo->lower_expression(function, inserter, &len); + gogo->flatten_expression(function, inserter, &len); + l2tmp = Statement::make_temporary(int_type, len, loc); + inserter->insert(l2tmp); + + // len2 = l2tmp + len2 = Expression::make_temporary_reference(l2tmp, loc); + } + else + { + // We have to ensure that all the arguments are in variables + // now, because otherwise if one of them is an index expression + // into the current slice we could overwrite it before we fetch + // it. + add = new Expression_list(); + Expression_list::const_iterator pa = args->begin(); + for (++pa; pa != args->end(); ++pa) + { + if ((*pa)->is_variable()) + add->push_back(*pa); + else + { + Temporary_statement* tmp = Statement::make_temporary(NULL, *pa, + loc); + inserter->insert(tmp); + add->push_back(Expression::make_temporary_reference(tmp, loc)); + } + } + + // len2 = len(add) + len2 = Expression::make_integer_ul(add->size(), int_type, loc); + } + + // ntmp := l1tmp + len2 + Expression* ref = Expression::make_temporary_reference(l1tmp, loc); + Expression* sum = Expression::make_binary(OPERATOR_PLUS, ref, len2, loc); + gogo->lower_expression(function, inserter, &sum); + gogo->flatten_expression(function, inserter, &sum); + Temporary_statement* ntmp = Statement::make_temporary(int_type, sum, loc); + inserter->insert(ntmp); + + // s1tmp = uint(ntmp) > uint(cap(s1tmp)) ? + // growslice(type, s1tmp, ntmp) : + // s1tmp[:ntmp] + // Using uint here means that if the computation of ntmp overflowed, + // we will call growslice which will panic. + + Expression* left = Expression::make_temporary_reference(ntmp, loc); + left = Expression::make_cast(uint_type, left, loc); + + Named_object* capfn = gogo->lookup_global("cap"); + Expression* capref = Expression::make_func_reference(capfn, NULL, loc); + call_args = new Expression_list(); + call_args->push_back(Expression::make_temporary_reference(s1tmp, loc)); + Expression* right = Expression::make_call(capref, call_args, false, loc); + right = Expression::make_cast(uint_type, right, loc); + + Expression* cond = Expression::make_binary(OPERATOR_GT, left, right, loc); + + Expression* a1 = Expression::make_type_descriptor(element_type, loc); + Expression* a2 = Expression::make_temporary_reference(s1tmp, loc); + Expression* a3 = Expression::make_temporary_reference(ntmp, loc); + Expression* call = Runtime::make_call(Runtime::GROWSLICE, loc, 3, + a1, a2, a3); + call = Expression::make_unsafe_cast(slice_type, call, loc); + + ref = Expression::make_temporary_reference(s1tmp, loc); + Expression* zero = Expression::make_integer_ul(0, int_type, loc); + Expression* ref2 = Expression::make_temporary_reference(ntmp, loc); + // FIXME: Mark this index as not requiring bounds checks. + ref = Expression::make_index(ref, zero, ref2, NULL, loc); + + Expression* rhs = Expression::make_conditional(cond, call, ref, loc); + + gogo->lower_expression(function, inserter, &rhs); + gogo->flatten_expression(function, inserter, &rhs); + + Expression* lhs = Expression::make_temporary_reference(s1tmp, loc); + Statement* assign = Statement::make_assignment(lhs, rhs, loc); + inserter->insert(assign); + + if (this->is_varargs()) + { + // copy(s1tmp[l1tmp:], s2tmp) + a1 = Expression::make_temporary_reference(s1tmp, loc); + ref = Expression::make_temporary_reference(l1tmp, loc); + Expression* nil = Expression::make_nil(loc); + // FIXME: Mark this index as not requiring bounds checks. + a1 = Expression::make_index(a1, ref, nil, NULL, loc); + + a2 = Expression::make_temporary_reference(s2tmp, loc); + + Named_object* copyfn = gogo->lookup_global("copy"); + Expression* copyref = Expression::make_func_reference(copyfn, NULL, loc); + call_args = new Expression_list(); + call_args->push_back(a1); + call_args->push_back(a2); + call = Expression::make_call(copyref, call_args, false, loc); + gogo->lower_expression(function, inserter, &call); + gogo->flatten_expression(function, inserter, &call); + inserter->insert(Statement::make_statement(call, false)); + } + else + { + // For each argument: + // s1tmp[l1tmp+i] = a + unsigned long i = 0; + for (Expression_list::const_iterator pa = add->begin(); + pa != add->end(); + ++pa, ++i) + { + ref = Expression::make_temporary_reference(s1tmp, loc); + ref2 = Expression::make_temporary_reference(l1tmp, loc); + Expression* off = Expression::make_integer_ul(i, int_type, loc); + ref2 = Expression::make_binary(OPERATOR_PLUS, ref2, off, loc); + // FIXME: Mark this index as not requiring bounds checks. + lhs = Expression::make_index(ref, ref2, NULL, NULL, loc); + gogo->lower_expression(function, inserter, &lhs); + gogo->flatten_expression(function, inserter, &lhs); + assign = Statement::make_assignment(lhs, *pa, loc); + inserter->insert(assign); + } + } + + return Expression::make_temporary_reference(s1tmp, loc); +} + // Return whether an expression has an integer value. Report an error // if not. This is used when handling calls to the predeclared make // function. @@ -8011,6 +8186,7 @@ Builtin_call_expression::do_determine_type(const Type_context* context) bool is_print; Type* arg_type = NULL; + Type* trailing_arg_types = NULL; switch (this->code_) { case BUILTIN_PRINT: @@ -8047,6 +8223,16 @@ Builtin_call_expression::do_determine_type(const Type_context* context) } break; + case BUILTIN_APPEND: + if (!this->is_varargs() + && args != NULL + && !args->empty() + && args->front()->type()->is_slice_type()) + trailing_arg_types = + args->front()->type()->array_type()->element_type(); + is_print = false; + break; + default: is_print = false; break; @@ -8103,6 +8289,12 @@ Builtin_call_expression::do_determine_type(const Type_context* context) } (*pa)->determine_type(&subcontext); + + if (trailing_arg_types != NULL) + { + arg_type = trailing_arg_types; + trailing_arg_types = NULL; + } } } } @@ -8309,54 +8501,102 @@ Builtin_call_expression::do_check_types(Gogo*) case BUILTIN_APPEND: { const Expression_list* args = this->args(); - if (args == NULL || args->size() < 2) + if (args == NULL || args->empty()) { this->report_error(_("not enough arguments")); break; } - if (args->size() > 2) - { - this->report_error(_("too many arguments")); - break; - } - if (args->front()->type()->is_error() - || args->back()->type()->is_error()) + + Type* slice_type = args->front()->type(); + if (!slice_type->is_slice_type()) { + if (slice_type->is_error_type()) + break; + if (slice_type->is_nil_type()) + go_error_at(args->front()->location(), "use of untyped nil"); + else + go_error_at(args->front()->location(), + "argument 1 must be a slice"); this->set_is_error(); break; } - Array_type* at = args->front()->type()->array_type(); - Type* e = at->element_type(); - - // The language permits appending a string to a []byte, as a - // special case. - if (args->back()->type()->is_string_type()) + Type* element_type = slice_type->array_type()->element_type(); + if (this->is_varargs()) { - if (e->integer_type() != NULL && e->integer_type()->is_byte()) - break; - } + if (!args->back()->type()->is_slice_type() + && !args->back()->type()->is_string_type()) + { + go_error_at(args->back()->location(), + "invalid use of %<...%> with non-slice/non-string"); + this->set_is_error(); + break; + } - // The language says that the second argument must be - // assignable to a slice of the element type of the first - // argument. We already know the first argument is a slice - // type. - Type* arg2_type = Type::make_array_type(e, NULL); - std::string reason; - if (!Type::are_assignable(arg2_type, args->back()->type(), &reason)) - { - if (reason.empty()) - this->report_error(_("argument 2 has invalid type")); + if (args->size() < 2) + { + this->report_error(_("not enough arguments")); + break; + } + if (args->size() > 2) + { + this->report_error(_("too many arguments")); + break; + } + + if (args->back()->type()->is_string_type() + && element_type->integer_type() != NULL + && element_type->integer_type()->is_byte()) + { + // Permit append(s1, s2...) when s1 is a slice of + // bytes and s2 is a string type. + } else { - go_error_at(this->location(), - "argument 2 has invalid type (%s)", - reason.c_str()); - this->set_is_error(); + // We have to test for assignment compatibility to a + // slice of the element type, which is not necessarily + // the same as the type of the first argument: the + // first argument might have a named type. + Type* check_type = Type::make_array_type(element_type, NULL); + std::string reason; + if (!Type::are_assignable(check_type, args->back()->type(), + &reason)) + { + if (reason.empty()) + go_error_at(args->back()->location(), + "argument 2 has invalid type"); + else + go_error_at(args->back()->location(), + "argument 2 has invalid type (%s)", + reason.c_str()); + this->set_is_error(); + break; + } + } + } + else + { + Expression_list::const_iterator pa = args->begin(); + int i = 2; + for (++pa; pa != args->end(); ++pa, ++i) + { + std::string reason; + if (!Type::are_assignable(element_type, (*pa)->type(), + &reason)) + { + if (reason.empty()) + go_error_at((*pa)->location(), + "argument %d has incompatible type", i); + else + go_error_at((*pa)->location(), + "argument %d has incompatible type (%s)", + i, reason.c_str()); + this->set_is_error(); + } } } - break; } + break; case BUILTIN_REAL: case BUILTIN_IMAG: @@ -8719,97 +8959,39 @@ Builtin_call_expression::do_get_backend(Translate_context* context) Type* arg1_type = arg1->type(); Array_type* at = arg1_type->array_type(); go_assert(arg1->is_variable()); - Expression* arg1_val = at->get_value_pointer(gogo, arg1); - Expression* arg1_len = at->get_length(gogo, arg1); + + Expression* call; Type* arg2_type = arg2->type(); go_assert(arg2->is_variable()); - Expression* arg2_val; - Expression* arg2_len; - if (arg2_type->is_slice_type()) - { - at = arg2_type->array_type(); - arg2_val = at->get_value_pointer(gogo, arg2); - arg2_len = at->get_length(gogo, arg2); - } + if (arg2_type->is_string_type()) + call = Runtime::make_call(Runtime::SLICESTRINGCOPY, location, + 2, arg1, arg2); else { - go_assert(arg2->is_variable()); - arg2_val = Expression::make_string_info(arg2, STRING_INFO_DATA, - location); - arg2_len = Expression::make_string_info(arg2, STRING_INFO_LENGTH, - location); + Type* et = at->element_type(); + if (et->has_pointer()) + { + Expression* td = Expression::make_type_descriptor(et, + location); + call = Runtime::make_call(Runtime::TYPEDSLICECOPY, location, + 3, td, arg1, arg2); + } + else + { + Expression* sz = Expression::make_type_info(et, + TYPE_INFO_SIZE); + call = Runtime::make_call(Runtime::SLICECOPY, location, 3, + arg1, arg2, sz); + } } - Expression* cond = - Expression::make_binary(OPERATOR_LT, arg1_len, arg2_len, location); - Expression* length = - Expression::make_conditional(cond, arg1_len, arg2_len, location); - - Type* element_type = at->element_type(); - int64_t element_size; - bool ok = element_type->backend_type_size(gogo, &element_size); - if (!ok) - { - go_assert(saw_errors()); - return gogo->backend()->error_expression(); - } - - Expression* size_expr = Expression::make_integer_int64(element_size, - length->type(), - location); - Expression* bytecount = - Expression::make_binary(OPERATOR_MULT, size_expr, length, location); - Expression* copy = Runtime::make_call(Runtime::COPY, location, 3, - arg1_val, arg2_val, bytecount); - Expression* compound = Expression::make_compound(copy, length, location); - return compound->get_backend(context); + return call->get_backend(context); } case BUILTIN_APPEND: - { - const Expression_list* args = this->args(); - go_assert(args != NULL && args->size() == 2); - Expression* arg1 = args->front(); - Expression* arg2 = args->back(); - - Array_type* at = arg1->type()->array_type(); - Type* element_type = at->element_type()->forwarded(); - - go_assert(arg2->is_variable()); - Expression* arg2_val; - Expression* arg2_len; - int64_t size; - if (arg2->type()->is_string_type() - && element_type->integer_type() != NULL - && element_type->integer_type()->is_byte()) - { - arg2_val = Expression::make_string_info(arg2, STRING_INFO_DATA, - location); - arg2_len = Expression::make_string_info(arg2, STRING_INFO_LENGTH, - location); - size = 1; - } - else - { - arg2_val = at->get_value_pointer(gogo, arg2); - arg2_len = at->get_length(gogo, arg2); - bool ok = element_type->backend_type_size(gogo, &size); - if (!ok) - { - go_assert(saw_errors()); - return gogo->backend()->error_expression(); - } - } - Expression* element_size = - Expression::make_integer_int64(size, NULL, location); - - Expression* append = Runtime::make_call(Runtime::APPEND, location, 4, - arg1, arg2_val, arg2_len, - element_size); - append = Expression::make_unsafe_cast(arg1->type(), append, location); - return append->get_backend(context); - } + // Handled in Builtin_call_expression::flatten_append. + go_unreachable(); case BUILTIN_REAL: case BUILTIN_IMAG: diff --git a/gcc/go/gofrontend/runtime.cc b/gcc/go/gofrontend/runtime.cc index 4072920..77c48ec 100644 --- a/gcc/go/gofrontend/runtime.cc +++ b/gcc/go/gofrontend/runtime.cc @@ -425,9 +425,9 @@ Runtime::name_to_code(const std::string& name) else if (name == "close") code = Runtime::CLOSE; else if (name == "copy") - code = Runtime::COPY; + code = Runtime::SLICECOPY; else if (name == "append") - code = Runtime::APPEND; + code = Runtime::GROWSLICE; else if (name == "delete") code = Runtime::MAPDELETE; else diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 3051624b..5d3ce677 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -87,12 +87,7 @@ DEF_GO_RUNTIME(COMPLEX128_DIV, "__go_complex128_div", P2(COMPLEX128, COMPLEX128), R1(COMPLEX128)) // Make a slice. -DEF_GO_RUNTIME(MAKESLICE1, "__go_make_slice1", P2(TYPE, UINTPTR), R1(SLICE)) -DEF_GO_RUNTIME(MAKESLICE2, "__go_make_slice2", P3(TYPE, UINTPTR, UINTPTR), - R1(SLICE)) -DEF_GO_RUNTIME(MAKESLICE1BIG, "__go_make_slice1_big", P2(TYPE, UINT64), - R1(SLICE)) -DEF_GO_RUNTIME(MAKESLICE2BIG, "__go_make_slice2_big", P3(TYPE, UINT64, UINT64), +DEF_GO_RUNTIME(MAKESLICE, "runtime.makeslice", P3(TYPE, INT64, INT64), R1(SLICE)) @@ -211,11 +206,20 @@ DEF_GO_RUNTIME(CLOSE, "runtime.closechan", P1(CHAN), R0()) // Copy. -DEF_GO_RUNTIME(COPY, "__go_copy", P3(POINTER, POINTER, UINTPTR), R0()) +DEF_GO_RUNTIME(SLICECOPY, "runtime.slicecopy", P3(SLICE, SLICE, UINTPTR), + R1(INT)) -// Append. -DEF_GO_RUNTIME(APPEND, "__go_append", P4(SLICE, POINTER, UINTPTR, UINTPTR), - R1(SLICE)) +// Copy from string. +DEF_GO_RUNTIME(SLICESTRINGCOPY, "runtime.slicestringcopy", P2(SLICE, STRING), + R1(INT)) + +// Copy of value containing pointers. +DEF_GO_RUNTIME(TYPEDSLICECOPY, "runtime.typedslicecopy", + P3(TYPE, SLICE, SLICE), R1(INT)) + + +// Grow a slice for append. +DEF_GO_RUNTIME(GROWSLICE, "runtime.growslice", P3(TYPE, SLICE, INT), R1(SLICE)) // Register roots (global variables) for the garbage collector. |