diff mbox

Go patch committed: copy slice from Go 1.7 runtime

Message ID CAOyqgcUuk+O+n+5rNU=22nMmWscCh3jvt1C+RF2Shg+6Z+VfOQ@mail.gmail.com
State New
Headers show

Commit Message

Ian Lance Taylor Oct. 28, 2016, 10:34 p.m. UTC
This patch to the compiler and libgo copies the slice support from the
Go 1.7 runtime.

This changes the Go frontend to 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.

This renames 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.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian
diff mbox

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 241661)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -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: gcc/go/gofrontend/escape.cc
===================================================================
--- gcc/go/gofrontend/escape.cc	(revision 241384)
+++ gcc/go/gofrontend/escape.cc	(working copy)
@@ -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) co
 	  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(Expre
 		}
 		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(Expre
 		}
 		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(Expre
 
 	      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
 	      {
 		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
 
 		  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
 		{
 		  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
 
 		    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: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc	(revision 241384)
+++ gcc/go/gofrontend/expressions.cc	(working copy)
@@ -6951,7 +6951,9 @@  class Builtin_call_expression : public C
   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_
 // 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*
       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*
 	}
       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*
 // 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
       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
 
     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
 // 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_ty
 
   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_ty
       }
       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_ty
 	    }
 
 	  (*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(
     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(
 	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: gcc/go/gofrontend/runtime.cc
===================================================================
--- gcc/go/gofrontend/runtime.cc	(revision 241341)
+++ gcc/go/gofrontend/runtime.cc	(working copy)
@@ -425,9 +425,9 @@  Runtime::name_to_code(const std::string&
   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: gcc/go/gofrontend/runtime.def
===================================================================
--- gcc/go/gofrontend/runtime.def	(revision 241384)
+++ gcc/go/gofrontend/runtime.def	(working copy)
@@ -87,12 +87,7 @@  DEF_GO_RUNTIME(COMPLEX128_DIV, "__go_com
 	       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
 
 
 // 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: libgo/Makefile.am
===================================================================
--- libgo/Makefile.am	(revision 241432)
+++ libgo/Makefile.am	(working copy)
@@ -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: libgo/Makefile.in
===================================================================
--- libgo/Makefile.in	(revision 241432)
+++ libgo/Makefile.in	(working copy)
@@ -237,23 +237,22 @@  libgo_llgo_la_DEPENDENCIES = $(am__DEPEN
 @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/
 @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-construc
 @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.
 @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
Index: libgo/go/runtime/slice.go
===================================================================
--- libgo/go/runtime/slice.go	(revision 0)
+++ libgo/go/runtime/slice.go	(working copy)
@@ -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: libgo/go/runtime/stubs.go
===================================================================
--- libgo/go/runtime/stubs.go	(revision 241384)
+++ libgo/go/runtime/stubs.go	(working copy)
@@ -253,11 +253,18 @@  func typedmemmove(typ *_type, dst, src u
 	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, ne
 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
+}
Index: libgo/runtime/go-append.c
===================================================================
--- libgo/runtime/go-append.c	(revision 241341)
+++ libgo/runtime/go-append.c	(working copy)
@@ -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;
-}
Index: libgo/runtime/go-copy.c
===================================================================
--- libgo/runtime/go-copy.c	(revision 241341)
+++ libgo/runtime/go-copy.c	(working copy)
@@ -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);
-}
Index: libgo/runtime/go-make-slice.c
===================================================================
--- libgo/runtime/go-make-slice.c	(revision 241341)
+++ libgo/runtime/go-make-slice.c	(working copy)
@@ -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: libgo/runtime/malloc.goc
===================================================================
--- libgo/runtime/malloc.goc	(revision 241384)
+++ libgo/runtime/malloc.goc	(working copy)
@@ -81,7 +81,7 @@  runtime_mallocgc(uintptr size, uintptr t
 		// 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 obj
 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: libgo/runtime/runtime.h
===================================================================
--- libgo/runtime/runtime.h	(revision 241427)
+++ libgo/runtime/runtime.h	(working copy)
@@ -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;