compiler, runtime: copy slice code from Go 1.7 runtime
authorIan Lance Taylor <ian@gcc.gnu.org>
Fri, 28 Oct 2016 22:34:47 +0000 (22:34 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Fri, 28 Oct 2016 22:34:47 +0000 (22:34 +0000)
    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

14 files changed:
gcc/go/gofrontend/MERGE
gcc/go/gofrontend/escape.cc
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/runtime.cc
gcc/go/gofrontend/runtime.def
libgo/Makefile.am
libgo/Makefile.in
libgo/go/runtime/slice.go [new file with mode: 0644]
libgo/go/runtime/stubs.go
libgo/runtime/go-append.c [deleted file]
libgo/runtime/go-copy.c [deleted file]
libgo/runtime/go-make-slice.c [deleted file]
libgo/runtime/malloc.goc
libgo/runtime/runtime.h

index e2fb0db49b80f2efdb4d667bd49a8c7baa7043c0..4d047056cbb6105b557cdb600b8e6e9a948eccef 100644 (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.
index ba55ea314c3458176b347c11bc46a63a308ce920..bcea63d5a6e9a84613697eb510dbe868caec9a3b 100644 (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:
index 241dc36e5e1350578ce7173bf1e46a7e279c871a..7f9d365b6e23a9af1f9f87383dba1d0c44bdbef4 100644 (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:
index 4072920b9d25f4a1d41d58b7c88445aaa462754f..77c48ecbaaf70b4e710ca1408beadd421ea4fac3 100644 (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
index 3051624b66c32d8c15af7dd4d32b9d21705a076d..5d3ce67725d4890589f4a28e977827138bca55a7 100644 (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.
index 2e46ecd514f8d899d346693cfb66607320c4b281..8237bb6af4d3f9c58d5855fea22083dea718f0fb 100644 (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 \
index 25b5a7be8ebbdbd1987889ce28dd98cee3d0a221..50fc3f63f3c7c20cfd7453416c699067b6c7c14b 100644 (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
diff --git a/libgo/go/runtime/slice.go b/libgo/go/runtime/slice.go
new file mode 100644 (file)
index 0000000..4548a5b
--- /dev/null
@@ -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
+}
index 5924ee63dc7c680331851a576abca967556e15c7..755933de713db56be3ba56ab7dd12aa1a24dd240 100644 (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
+}
diff --git a/libgo/runtime/go-append.c b/libgo/runtime/go-append.c
deleted file mode 100644 (file)
index 1b2d49e..0000000
+++ /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 (file)
index 05e16ac..0000000
+++ /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 <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);
-}
diff --git a/libgo/runtime/go-make-slice.c b/libgo/runtime/go-make-slice.c
deleted file mode 100644 (file)
index ccd07e5..0000000
+++ /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 <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);
-}
index 1fd44614227fdccd054a324368d95a351f05fa9d..5cbdc4632fbdc801bcf88f6f8975a9493e22d911 100644 (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() {
index 8be0df4c17deed3d1a165f86daf76f15772da94f..501f1b41ace4f89b08571a6020f1f2b60236ff83 100644 (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;