From: Ian Lance Taylor Date: Fri, 28 Oct 2016 22:34:47 +0000 (+0000) Subject: compiler, runtime: copy slice code from Go 1.7 runtime X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=94f56408db96e2e12f6e1322ed2c1c465df934f6;p=gcc.git compiler, runtime: copy slice code from Go 1.7 runtime Change the compiler handle append as the gc compiler does: call a function to grow the slice, but otherwise assign the new elements directly to the final slice. For the current gccgo memory allocator the slice code has to call runtime_newarray, not mallocgc directly, so that the allocator sets the TypeInfo_Array bit in the type pointer. Rename the static function cnew to runtime_docnew, so that the stack trace ignores it when ignoring runtime functions. This was needed to fix the runtime/pprof tests on 386. Reviewed-on: https://go-review.googlesource.com/32218 From-SVN: r241667 --- diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index e2fb0db49b8..4d047056cbb 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 ba55ea314c3..bcea63d5a6e 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 241dc36e5e1..7f9d365b6e2 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 4072920b9d2..77c48ecbaaf 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 3051624b66c..5d3ce67725d 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. diff --git a/libgo/Makefile.am b/libgo/Makefile.am index 2e46ecd514f..8237bb6af4d 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -428,7 +428,6 @@ endif endif runtime_files = \ - runtime/go-append.c \ runtime/go-assert.c \ runtime/go-breakpoint.c \ runtime/go-caller.c \ @@ -436,12 +435,10 @@ runtime_files = \ runtime/go-cdiv.c \ runtime/go-cgo.c \ runtime/go-construct-map.c \ - runtime/go-copy.c \ runtime/go-defer.c \ runtime/go-deferred-recover.c \ runtime/go-ffi.c \ runtime/go-fieldtrack.c \ - runtime/go-make-slice.c \ runtime/go-matherr.c \ runtime/go-memclr.c \ runtime/go-memcmp.c \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 25b5a7be8eb..50fc3f63f3c 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -237,23 +237,22 @@ libgo_llgo_la_DEPENDENCIES = $(am__DEPENDENCIES_4) @LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_LINUX_FALSE@am__objects_4 = \ @LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_LINUX_FALSE@ getncpu-bsd.lo @LIBGO_IS_LINUX_TRUE@am__objects_4 = getncpu-linux.lo -am__objects_5 = go-append.lo go-assert.lo go-breakpoint.lo \ - go-caller.lo go-callers.lo go-cdiv.lo go-cgo.lo \ - go-construct-map.lo go-copy.lo go-defer.lo \ - go-deferred-recover.lo go-ffi.lo go-fieldtrack.lo \ - go-make-slice.lo go-matherr.lo go-memclr.lo go-memcmp.lo \ - go-memequal.lo go-memmove.lo go-nanotime.lo go-now.lo \ - go-new.lo go-nosys.lo go-panic.lo go-recover.lo \ - go-reflect-call.lo go-runtime-error.lo go-setenv.lo \ - go-signal.lo go-strslice.lo go-type-complex.lo \ - go-type-float.lo go-type-identity.lo go-type-string.lo \ - go-typedesc-equal.lo go-unsafe-new.lo go-unsafe-newarray.lo \ - go-unsafe-pointer.lo go-unsetenv.lo go-unwind.lo go-varargs.lo \ - env_posix.lo heapdump.lo mcache.lo mcentral.lo \ - $(am__objects_1) mfixalloc.lo mgc0.lo mheap.lo msize.lo \ - panic.lo parfor.lo print.lo proc.lo runtime.lo signal_unix.lo \ - thread.lo $(am__objects_2) yield.lo $(am__objects_3) malloc.lo \ - runtime1.lo sigqueue.lo $(am__objects_4) +am__objects_5 = go-assert.lo go-breakpoint.lo go-caller.lo \ + go-callers.lo go-cdiv.lo go-cgo.lo go-construct-map.lo \ + go-defer.lo go-deferred-recover.lo go-ffi.lo go-fieldtrack.lo \ + go-matherr.lo go-memclr.lo go-memcmp.lo go-memequal.lo \ + go-memmove.lo go-nanotime.lo go-now.lo go-new.lo go-nosys.lo \ + go-panic.lo go-recover.lo go-reflect-call.lo \ + go-runtime-error.lo go-setenv.lo go-signal.lo go-strslice.lo \ + go-type-complex.lo go-type-float.lo go-type-identity.lo \ + go-type-string.lo go-typedesc-equal.lo go-unsafe-new.lo \ + go-unsafe-newarray.lo go-unsafe-pointer.lo go-unsetenv.lo \ + go-unwind.lo go-varargs.lo env_posix.lo heapdump.lo mcache.lo \ + mcentral.lo $(am__objects_1) mfixalloc.lo mgc0.lo mheap.lo \ + msize.lo panic.lo parfor.lo print.lo proc.lo runtime.lo \ + signal_unix.lo thread.lo $(am__objects_2) yield.lo \ + $(am__objects_3) malloc.lo runtime1.lo sigqueue.lo \ + $(am__objects_4) am_libgo_llgo_la_OBJECTS = $(am__objects_5) libgo_llgo_la_OBJECTS = $(am_libgo_llgo_la_OBJECTS) libgo_llgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -824,7 +823,6 @@ toolexeclibgounicode_DATA = \ @LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_LINUX_FALSE@runtime_getncpu_file = runtime/getncpu-bsd.c @LIBGO_IS_LINUX_TRUE@runtime_getncpu_file = runtime/getncpu-linux.c runtime_files = \ - runtime/go-append.c \ runtime/go-assert.c \ runtime/go-breakpoint.c \ runtime/go-caller.c \ @@ -832,12 +830,10 @@ runtime_files = \ runtime/go-cdiv.c \ runtime/go-cgo.c \ runtime/go-construct-map.c \ - runtime/go-copy.c \ runtime/go-defer.c \ runtime/go-deferred-recover.c \ runtime/go-ffi.c \ runtime/go-fieldtrack.c \ - runtime/go-make-slice.c \ runtime/go-matherr.c \ runtime/go-memclr.c \ runtime/go-memcmp.c \ @@ -1519,7 +1515,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-none.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-solaris.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-append.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-assert.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-breakpoint.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-caller.Plo@am__quote@ @@ -1527,12 +1522,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-cdiv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-cgo.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-construct-map.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-copy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-defer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-deferred-recover.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-ffi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-fieldtrack.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-make-slice.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-matherr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-memclr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-memcmp.Plo@am__quote@ @@ -1650,13 +1643,6 @@ libgolibbegin_a-go-libmain.obj: runtime/go-libmain.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgolibbegin_a_CFLAGS) $(CFLAGS) -c -o libgolibbegin_a-go-libmain.obj `if test -f 'runtime/go-libmain.c'; then $(CYGPATH_W) 'runtime/go-libmain.c'; else $(CYGPATH_W) '$(srcdir)/runtime/go-libmain.c'; fi` -go-append.lo: runtime/go-append.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-append.lo -MD -MP -MF $(DEPDIR)/go-append.Tpo -c -o go-append.lo `test -f 'runtime/go-append.c' || echo '$(srcdir)/'`runtime/go-append.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-append.Tpo $(DEPDIR)/go-append.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-append.c' object='go-append.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-append.lo `test -f 'runtime/go-append.c' || echo '$(srcdir)/'`runtime/go-append.c - go-assert.lo: runtime/go-assert.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-assert.lo -MD -MP -MF $(DEPDIR)/go-assert.Tpo -c -o go-assert.lo `test -f 'runtime/go-assert.c' || echo '$(srcdir)/'`runtime/go-assert.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-assert.Tpo $(DEPDIR)/go-assert.Plo @@ -1706,13 +1692,6 @@ go-construct-map.lo: runtime/go-construct-map.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-construct-map.lo `test -f 'runtime/go-construct-map.c' || echo '$(srcdir)/'`runtime/go-construct-map.c -go-copy.lo: runtime/go-copy.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-copy.lo -MD -MP -MF $(DEPDIR)/go-copy.Tpo -c -o go-copy.lo `test -f 'runtime/go-copy.c' || echo '$(srcdir)/'`runtime/go-copy.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-copy.Tpo $(DEPDIR)/go-copy.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-copy.c' object='go-copy.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-copy.lo `test -f 'runtime/go-copy.c' || echo '$(srcdir)/'`runtime/go-copy.c - go-defer.lo: runtime/go-defer.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-defer.lo -MD -MP -MF $(DEPDIR)/go-defer.Tpo -c -o go-defer.lo `test -f 'runtime/go-defer.c' || echo '$(srcdir)/'`runtime/go-defer.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-defer.Tpo $(DEPDIR)/go-defer.Plo @@ -1741,13 +1720,6 @@ go-fieldtrack.lo: runtime/go-fieldtrack.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-fieldtrack.lo `test -f 'runtime/go-fieldtrack.c' || echo '$(srcdir)/'`runtime/go-fieldtrack.c -go-make-slice.lo: runtime/go-make-slice.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-make-slice.lo -MD -MP -MF $(DEPDIR)/go-make-slice.Tpo -c -o go-make-slice.lo `test -f 'runtime/go-make-slice.c' || echo '$(srcdir)/'`runtime/go-make-slice.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-make-slice.Tpo $(DEPDIR)/go-make-slice.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-make-slice.c' object='go-make-slice.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-make-slice.lo `test -f 'runtime/go-make-slice.c' || echo '$(srcdir)/'`runtime/go-make-slice.c - go-matherr.lo: runtime/go-matherr.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-matherr.lo -MD -MP -MF $(DEPDIR)/go-matherr.Tpo -c -o go-matherr.lo `test -f 'runtime/go-matherr.c' || echo '$(srcdir)/'`runtime/go-matherr.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-matherr.Tpo $(DEPDIR)/go-matherr.Plo diff --git a/libgo/go/runtime/slice.go b/libgo/go/runtime/slice.go new file mode 100644 index 00000000000..4548a5b2321 --- /dev/null +++ b/libgo/go/runtime/slice.go @@ -0,0 +1,212 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import ( + "unsafe" +) + +// For gccgo, use go:linkname to rename compiler-called functions to +// themselves, so that the compiler will export them. +// +//go:linkname makeslice runtime.makeslice +//go:linkname growslice runtime.growslice +//go:linkname slicecopy runtime.slicecopy +//go:linkname slicestringcopy runtime.slicestringcopy + +type slice struct { + array unsafe.Pointer + len int + cap int +} + +// maxElems is a lookup table containing the maximum capacity for a slice. +// The index is the size of the slice element. +var maxElems = [...]uintptr{ + ^uintptr(0), + _MaxMem / 1, _MaxMem / 2, _MaxMem / 3, _MaxMem / 4, + _MaxMem / 5, _MaxMem / 6, _MaxMem / 7, _MaxMem / 8, + _MaxMem / 9, _MaxMem / 10, _MaxMem / 11, _MaxMem / 12, + _MaxMem / 13, _MaxMem / 14, _MaxMem / 15, _MaxMem / 16, + _MaxMem / 17, _MaxMem / 18, _MaxMem / 19, _MaxMem / 20, + _MaxMem / 21, _MaxMem / 22, _MaxMem / 23, _MaxMem / 24, + _MaxMem / 25, _MaxMem / 26, _MaxMem / 27, _MaxMem / 28, + _MaxMem / 29, _MaxMem / 30, _MaxMem / 31, _MaxMem / 32, +} + +// maxSliceCap returns the maximum capacity for a slice. +func maxSliceCap(elemsize uintptr) uintptr { + if elemsize < uintptr(len(maxElems)) { + return maxElems[elemsize] + } + return _MaxMem / elemsize +} + +// TODO: take uintptrs instead of int64s? +func makeslice(et *_type, len64, cap64 int64) slice { + // NOTE: The len > maxElements check here is not strictly necessary, + // but it produces a 'len out of range' error instead of a 'cap out of range' error + // when someone does make([]T, bignumber). 'cap out of range' is true too, + // but since the cap is only being supplied implicitly, saying len is clearer. + // See issue 4085. + maxElements := maxSliceCap(et.size) + len := int(len64) + if len64 < 0 || int64(len) != len64 || uintptr(len) > maxElements { + panic(errorString("makeslice: len out of range")) + } + + cap := int(cap64) + if cap < len || int64(cap) != cap64 || uintptr(cap) > maxElements { + panic(errorString("makeslice: cap out of range")) + } + + // gccgo's current garbage collector requires using newarray, + // not mallocgc here. This can change back to mallocgc when + // we port the garbage collector. + p := newarray(et, cap) + return slice{p, len, cap} +} + +// growslice handles slice growth during append. +// It is passed the slice element type, the old slice, and the desired new minimum capacity, +// and it returns a new slice with at least that capacity, with the old data +// copied into it. +// The new slice's length is set to the requested capacity. +func growslice(et *_type, old slice, cap int) slice { + if raceenabled { + callerpc := getcallerpc(unsafe.Pointer(&et)) + racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice)) + } + if msanenabled { + msanread(old.array, uintptr(old.len*int(et.size))) + } + + if et.size == 0 { + if cap < old.cap { + panic(errorString("growslice: cap out of range")) + } + // append should not create a slice with nil pointer but non-zero len. + // We assume that append doesn't need to preserve old.array in this case. + return slice{unsafe.Pointer(&zerobase), cap, cap} + } + + newcap := old.cap + doublecap := newcap + newcap + if cap > doublecap { + newcap = cap + } else { + if old.len < 1024 { + newcap = doublecap + } else { + for newcap < cap { + newcap += newcap / 4 + } + } + } + + var lenmem, capmem uintptr + const ptrSize = unsafe.Sizeof((*byte)(nil)) + switch et.size { + case 1: + lenmem = uintptr(old.len) + capmem = roundupsize(uintptr(newcap)) + newcap = int(capmem) + case ptrSize: + lenmem = uintptr(old.len) * ptrSize + capmem = roundupsize(uintptr(newcap) * ptrSize) + newcap = int(capmem / ptrSize) + default: + lenmem = uintptr(old.len) * et.size + capmem = roundupsize(uintptr(newcap) * et.size) + newcap = int(capmem / et.size) + } + + if cap < old.cap || uintptr(newcap) > maxSliceCap(et.size) { + panic(errorString("growslice: cap out of range")) + } + + var p unsafe.Pointer + if et.kind&kindNoPointers != 0 { + // gccgo's current GC requires newarray, not mallocgc. + p = newarray(et, newcap) + memmove(p, old.array, lenmem) + // The call to memclr is not needed for gccgo since + // the newarray function will zero the memory. + // Calling memclr is also wrong since we allocated + // newcap*et.size bytes, which is not the same as capmem. + // memclr(add(p, lenmem), capmem-lenmem) + } else { + // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. + // gccgo's current GC requires newarray, not mallocgc. + p = newarray(et, newcap) + if !writeBarrier.enabled { + memmove(p, old.array, lenmem) + } else { + for i := uintptr(0); i < lenmem; i += et.size { + typedmemmove(et, add(p, i), add(old.array, i)) + } + } + } + + return slice{p, cap, newcap} +} + +func slicecopy(to, fm slice, width uintptr) int { + if fm.len == 0 || to.len == 0 { + return 0 + } + + n := fm.len + if to.len < n { + n = to.len + } + + if width == 0 { + return n + } + + if raceenabled { + callerpc := getcallerpc(unsafe.Pointer(&to)) + pc := funcPC(slicecopy) + racewriterangepc(to.array, uintptr(n*int(width)), callerpc, pc) + racereadrangepc(fm.array, uintptr(n*int(width)), callerpc, pc) + } + if msanenabled { + msanwrite(to.array, uintptr(n*int(width))) + msanread(fm.array, uintptr(n*int(width))) + } + + size := uintptr(n) * width + if size == 1 { // common case worth about 2x to do here + // TODO: is this still worth it with new memmove impl? + *(*byte)(to.array) = *(*byte)(fm.array) // known to be a byte pointer + } else { + memmove(to.array, fm.array, size) + } + return n +} + +func slicestringcopy(to []byte, fm string) int { + if len(fm) == 0 || len(to) == 0 { + return 0 + } + + n := len(fm) + if len(to) < n { + n = len(to) + } + + if raceenabled { + callerpc := getcallerpc(unsafe.Pointer(&to)) + pc := funcPC(slicestringcopy) + racewriterangepc(unsafe.Pointer(&to[0]), uintptr(n), callerpc, pc) + } + if msanenabled { + msanwrite(unsafe.Pointer(&to[0]), uintptr(n)) + } + + memmove(unsafe.Pointer(&to[0]), stringStructOf(&fm).str, uintptr(n)) + return n +} diff --git a/libgo/go/runtime/stubs.go b/libgo/go/runtime/stubs.go index 5924ee63dc7..755933de713 100644 --- a/libgo/go/runtime/stubs.go +++ b/libgo/go/runtime/stubs.go @@ -253,11 +253,18 @@ func typedmemmove(typ *_type, dst, src unsafe.Pointer) { memmove(dst, src, typ.size) } -// Here for gccgo unless and until we port slice.go. -type slice struct { - array unsafe.Pointer - len int - cap int +// Temporary for gccgo until we port mbarrier.go. +//go:linkname typedslicecopy runtime.typedslicecopy +func typedslicecopy(typ *_type, dst, src slice) int { + n := dst.len + if n > src.len { + n = src.len + } + if n == 0 { + return 0 + } + memmove(dst.array, src.array, uintptr(n)*typ.size) + return n } // Here for gccgo until we port malloc.go. @@ -474,3 +481,11 @@ func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) { func writebarrierptr(dst *uintptr, src uintptr) { *dst = src } + +// Temporary for gccgo until we port malloc.go +var zerobase uintptr + +//go:linkname getZerobase runtime.getZerobase +func getZerobase() *uintptr { + return &zerobase +} diff --git a/libgo/runtime/go-append.c b/libgo/runtime/go-append.c deleted file mode 100644 index 1b2d49e53c1..00000000000 --- a/libgo/runtime/go-append.c +++ /dev/null @@ -1,74 +0,0 @@ -/* go-append.c -- the go builtin append function. - - Copyright 2010 The Go Authors. All rights reserved. - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. */ - -#include "runtime.h" -#include "go-panic.h" -#include "go-type.h" -#include "array.h" -#include "arch.h" -#include "malloc.h" - -/* We should be OK if we don't split the stack here, since the only - libc functions we call are memcpy and memmove. If we don't do - this, we will always split the stack, because of memcpy and - memmove. */ -extern struct __go_open_array -__go_append (struct __go_open_array, void *, uintptr_t, uintptr_t) - __attribute__ ((no_split_stack)); - -struct __go_open_array -__go_append (struct __go_open_array a, void *bvalues, uintptr_t bcount, - uintptr_t element_size) -{ - uintptr_t ucount; - intgo count; - - if (bvalues == NULL || bcount == 0) - return a; - - ucount = (uintptr_t) a.__count + bcount; - count = (intgo) ucount; - if ((uintptr_t) count != ucount || count <= a.__count) - runtime_panicstring ("append: slice overflow"); - - if (count > a.__capacity) - { - intgo m; - uintptr capmem; - void *n; - - m = a.__capacity; - if (m + m < count) - m = count; - else - { - do - { - if (a.__count < 1024) - m += m; - else - m += m / 4; - } - while (m < count); - } - - if (element_size > 0 && (uintptr) m > MaxMem / element_size) - runtime_panicstring ("growslice: cap out of range"); - - capmem = runtime_roundupsize (m * element_size); - - n = __go_alloc (capmem); - __builtin_memcpy (n, a.__values, a.__count * element_size); - - a.__values = n; - a.__capacity = m; - } - - __builtin_memmove ((char *) a.__values + a.__count * element_size, - bvalues, bcount * element_size); - a.__count = count; - return a; -} diff --git a/libgo/runtime/go-copy.c b/libgo/runtime/go-copy.c deleted file mode 100644 index 05e16acbf1c..00000000000 --- a/libgo/runtime/go-copy.c +++ /dev/null @@ -1,22 +0,0 @@ -/* go-append.c -- the go builtin copy function. - - Copyright 2010 The Go Authors. All rights reserved. - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. */ - -#include -#include - -/* We should be OK if we don't split the stack here, since we are just - calling memmove which shouldn't need much stack. If we don't do - this we will always split the stack, because of memmove. */ - -extern void -__go_copy (void *, void *, uintptr_t) - __attribute__ ((no_split_stack)); - -void -__go_copy (void *a, void *b, uintptr_t len) -{ - __builtin_memmove (a, b, len); -} diff --git a/libgo/runtime/go-make-slice.c b/libgo/runtime/go-make-slice.c deleted file mode 100644 index ccd07e5ac51..00000000000 --- a/libgo/runtime/go-make-slice.c +++ /dev/null @@ -1,99 +0,0 @@ -/* go-make-slice.c -- make a slice. - - Copyright 2011 The Go Authors. All rights reserved. - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. */ - -#include - -#include "runtime.h" -#include "go-alloc.h" -#include "go-assert.h" -#include "go-panic.h" -#include "go-type.h" -#include "array.h" -#include "arch.h" -#include "malloc.h" - -/* Dummy word to use as base pointer for make([]T, 0). - Since you cannot take the address of such a slice, - you can't tell that they all have the same base pointer. */ -uintptr runtime_zerobase; - -struct __go_open_array -__go_make_slice2 (const struct __go_type_descriptor *td, uintptr_t len, - uintptr_t cap) -{ - const struct __go_slice_type* std; - intgo ilen; - intgo icap; - uintptr_t size; - struct __go_open_array ret; - - __go_assert ((td->__code & GO_CODE_MASK) == GO_SLICE); - std = (const struct __go_slice_type *) td; - - ilen = (intgo) len; - if (ilen < 0 - || (uintptr_t) ilen != len - || (std->__element_type->__size > 0 - && len > MaxMem / std->__element_type->__size)) - runtime_panicstring ("makeslice: len out of range"); - - icap = (intgo) cap; - if (cap < len - || (uintptr_t) icap != cap - || (std->__element_type->__size > 0 - && cap > MaxMem / std->__element_type->__size)) - runtime_panicstring ("makeslice: cap out of range"); - - ret.__count = ilen; - ret.__capacity = icap; - - size = cap * std->__element_type->__size; - - if (size == 0) - ret.__values = &runtime_zerobase; - else if ((std->__element_type->__code & GO_NO_POINTERS) != 0) - ret.__values = - runtime_mallocgc (size, - (uintptr) std->__element_type | TypeInfo_Array, - FlagNoScan); - else - ret.__values = - runtime_mallocgc (size, - (uintptr) std->__element_type | TypeInfo_Array, - 0); - - return ret; -} - -struct __go_open_array -__go_make_slice1 (const struct __go_type_descriptor *td, uintptr_t len) -{ - return __go_make_slice2 (td, len, len); -} - -struct __go_open_array -__go_make_slice2_big (const struct __go_type_descriptor *td, uint64_t len, - uint64_t cap) -{ - uintptr_t slen; - uintptr_t scap; - - slen = (uintptr_t) len; - if ((uint64_t) slen != len) - runtime_panicstring ("makeslice: len out of range"); - - scap = (uintptr_t) cap; - if ((uint64_t) scap != cap) - runtime_panicstring ("makeslice: cap out of range"); - - return __go_make_slice2 (td, slen, scap); -} - -struct __go_open_array -__go_make_slice1_big (const struct __go_type_descriptor *td, uint64_t len) -{ - return __go_make_slice2_big (td, len, len); -} diff --git a/libgo/runtime/malloc.goc b/libgo/runtime/malloc.goc index 1fd44614227..5cbdc4632fb 100644 --- a/libgo/runtime/malloc.goc +++ b/libgo/runtime/malloc.goc @@ -81,7 +81,7 @@ runtime_mallocgc(uintptr size, uintptr typ, uint32 flag) // All 0-length allocations use this pointer. // The language does not require the allocations to // have distinct values. - return &runtime_zerobase; + return runtime_getZerobase(); } g = runtime_g(); @@ -881,7 +881,7 @@ func new(typ *Type) (ret *uint8) { } static void* -cnew(const Type *typ, intgo n, int32 objtyp) +runtime_docnew(const Type *typ, intgo n, int32 objtyp) { if((objtyp&(PtrSize-1)) != objtyp) runtime_throw("runtime: invalid objtyp"); @@ -894,13 +894,13 @@ cnew(const Type *typ, intgo n, int32 objtyp) void* runtime_cnew(const Type *typ) { - return cnew(typ, 1, TypeInfo_SingleObject); + return runtime_docnew(typ, 1, TypeInfo_SingleObject); } void* runtime_cnewarray(const Type *typ, intgo n) { - return cnew(typ, n, TypeInfo_Array); + return runtime_docnew(typ, n, TypeInfo_Array); } func GC() { diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 8be0df4c17d..501f1b41ace 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -234,7 +234,8 @@ enum /* * external data */ -extern uintptr runtime_zerobase; +extern uintptr* runtime_getZerobase(void) + __asm__(GOSYM_PREFIX "runtime.getZerobase"); extern G** runtime_allg; extern uintptr runtime_allglen; extern G* runtime_lastg;