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
This commit is contained in:
Ian Lance Taylor 2016-10-28 22:34:47 +00:00
parent 21f1031d6c
commit 94f56408db
14 changed files with 661 additions and 472 deletions

View File

@ -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.

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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.

View File

@ -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 \

View File

@ -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

212
libgo/go/runtime/slice.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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 <stddef.h>
#include <stdint.h>
/* 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);
}

View File

@ -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 <stdint.h>
#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);
}

View File

@ -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() {

View File

@ -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;