diff mbox

Go patch committed: copy string code from Go 1.7 runtime

Message ID CAOyqgcU2G_ZG+vA0CX0iuidR+frsux-G=HK1GXUHkEz7t1qu=g@mail.gmail.com
State New
Headers show

Commit Message

Ian Lance Taylor Oct. 12, 2016, 6:17 p.m. UTC
This patch to the Go frontend and libgo copies the string code from
the Go 1.7 runtime.  This mainly replaces some C code with Go code.

This adds compiler support for turning concatenating strings into a
call to a runtime function that takes the appropriate number of
arguments.

This renames some local variables in mgc0.c to avoid macros that the
new rune.go causes to appear in runtime.inc.

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 241072)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@ 
-c18d9f0e7270144ebd1f67d85995f434bbdab0b0
+f38ba8837a0c961e18d982930e192132870f3836
 
 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 240942)
+++ gcc/go/gofrontend/escape.cc	(working copy)
@@ -1233,13 +1233,17 @@  Escape_analysis_assign::expression(Expre
 	      case Runtime::MAKESLICE2:
 	      case Runtime::MAKESLICE1BIG:
 	      case Runtime::MAKESLICE2BIG:
-	      case Runtime::BYTE_ARRAY_TO_STRING:
-	      case Runtime::INT_ARRAY_TO_STRING:
-	      case Runtime::STRING_TO_BYTE_ARRAY:
-	      case Runtime::STRING_TO_INT_ARRAY:
-	      case Runtime::STRING_PLUS:
+	      case Runtime::SLICEBYTETOSTRING:
+	      case Runtime::SLICERUNETOSTRING:
+	      case Runtime::STRINGTOSLICEBYTE:
+	      case Runtime::STRINGTOSLICERUNE:
+	      case Runtime::CONCATSTRINGS:
+	      case Runtime::CONCATSTRING2:
+	      case Runtime::CONCATSTRING3:
+	      case Runtime::CONCATSTRING4:
+	      case Runtime::CONCATSTRING5:
 	      case Runtime::CONSTRUCT_MAP:
-	      case Runtime::INT_TO_STRING:
+	      case Runtime::INTSTRING:
 		{
 		  Node* runtime_node = Node::make_node(fe);
 		  this->context_->track(runtime_node);
@@ -1842,21 +1846,25 @@  Escape_analysis_assign::assign(Node* dst
 		  case Runtime::MAKESLICE1BIG:
 		  case Runtime::MAKESLICE2BIG:
 		    // DST = make(...).
-		  case Runtime::BYTE_ARRAY_TO_STRING:
+		  case Runtime::SLICEBYTETOSTRING:
 		    // DST = string([]byte{...}).
-		  case Runtime::INT_ARRAY_TO_STRING:
+		  case Runtime::SLICERUNETOSTRING:
 		    // DST = string([]int{...}).
-		  case Runtime::STRING_TO_BYTE_ARRAY:
+		  case Runtime::STRINGTOSLICEBYTE:
 		    // DST = []byte(str).
-		  case Runtime::STRING_TO_INT_ARRAY:
-		    // DST = []int(str).
-		  case Runtime::STRING_PLUS:
+		  case Runtime::STRINGTOSLICERUNE:
+		    // DST = []rune(str).
+		  case Runtime::CONCATSTRINGS:
+		  case Runtime::CONCATSTRING2:
+		  case Runtime::CONCATSTRING3:
+		  case Runtime::CONCATSTRING4:
+		  case Runtime::CONCATSTRING5:
 		    // DST = str1 + str2
 		  case Runtime::CONSTRUCT_MAP:
 		    // When building a map literal's backend representation.
 		    // Likely never seen here and covered in
 		    // Expression::EXPRESSION_MAP_CONSTRUCTION.
-		  case Runtime::INT_TO_STRING:
+		  case Runtime::INTSTRING:
 		    // DST = string(i).
 		  case Runtime::IFACEE2E2:
 		  case Runtime::IFACEI2E2:
@@ -2614,13 +2622,17 @@  Escape_analysis_flood::flood(Level level
 		    case Runtime::MAKESLICE2:
 		    case Runtime::MAKESLICE1BIG:
 		    case Runtime::MAKESLICE2BIG:
-		    case Runtime::BYTE_ARRAY_TO_STRING:
-		    case Runtime::INT_ARRAY_TO_STRING:
-		    case Runtime::STRING_TO_BYTE_ARRAY:
-		    case Runtime::STRING_TO_INT_ARRAY:
-		    case Runtime::STRING_PLUS:
+		    case Runtime::SLICEBYTETOSTRING:
+		    case Runtime::SLICERUNETOSTRING:
+		    case Runtime::STRINGTOSLICEBYTE:
+		    case Runtime::STRINGTOSLICERUNE:
+		    case Runtime::CONCATSTRINGS:
+		    case Runtime::CONCATSTRING2:
+		    case Runtime::CONCATSTRING3:
+		    case Runtime::CONCATSTRING4:
+		    case Runtime::CONCATSTRING5:
 		    case Runtime::CONSTRUCT_MAP:
-		    case Runtime::INT_TO_STRING:
+		    case Runtime::INTSTRING:
 		    case Runtime::CONVERT_INTERFACE:
 		      // All runtime calls that involve allocation of memory
 		      // except new.  Runtime::NEW gets lowered into an
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc	(revision 240956)
+++ gcc/go/gofrontend/expressions.cc	(working copy)
@@ -3419,7 +3419,8 @@  Type_conversion_expression::do_get_backe
 	}
 
       Expression* i2s_expr =
-          Runtime::make_call(Runtime::INT_TO_STRING, loc, 1, this->expr_);
+          Runtime::make_call(Runtime::INTSTRING, loc, 2,
+			     Expression::make_nil(loc), this->expr_);
       return Expression::make_cast(type, i2s_expr, loc)->get_backend(context);
     }
   else if (type->is_string_type() && expr_type->is_slice_type())
@@ -3431,16 +3432,14 @@  Type_conversion_expression::do_get_backe
 
       Runtime::Function code;
       if (e->integer_type()->is_byte())
-        code = Runtime::BYTE_ARRAY_TO_STRING;
+        code = Runtime::SLICEBYTETOSTRING;
       else
         {
           go_assert(e->integer_type()->is_rune());
-          code = Runtime::INT_ARRAY_TO_STRING;
+          code = Runtime::SLICERUNETOSTRING;
         }
-      Expression* valptr = a->get_value_pointer(gogo, this->expr_);
-      Expression* len = a->get_length(gogo, this->expr_);
-      return Runtime::make_call(code, loc, 2, valptr,
-				len)->get_backend(context);
+      return Runtime::make_call(code, loc, 2, Expression::make_nil(loc),
+				this->expr_)->get_backend(context);
     }
   else if (type->is_slice_type() && expr_type->is_string_type())
     {
@@ -3449,13 +3448,15 @@  Type_conversion_expression::do_get_backe
 
       Runtime::Function code;
       if (e->integer_type()->is_byte())
-	code = Runtime::STRING_TO_BYTE_ARRAY;
+	code = Runtime::STRINGTOSLICEBYTE;
       else
 	{
 	  go_assert(e->integer_type()->is_rune());
-	  code = Runtime::STRING_TO_INT_ARRAY;
+	  code = Runtime::STRINGTOSLICERUNE;
 	}
-      Expression* s2a = Runtime::make_call(code, loc, 1, this->expr_);
+      Expression* s2a = Runtime::make_call(code, loc, 2,
+					   Expression::make_nil(loc),
+					   this->expr_);
       return Expression::make_unsafe_cast(type, s2a, loc)->get_backend(context);
     }
   else if (type->is_numeric_type())
@@ -5068,6 +5069,31 @@  Binary_expression::do_lower(Gogo* gogo,
 	return this->lower_interface_value_comparison(gogo, inserter);
     }
 
+  // Lower string concatenation to String_concat_expression, so that
+  // we can group sequences of string additions.
+  if (this->left_->type()->is_string_type() && this->op_ == OPERATOR_PLUS)
+    {
+      Expression_list* exprs;
+      String_concat_expression* left_sce =
+	this->left_->string_concat_expression();
+      if (left_sce != NULL)
+	exprs = left_sce->exprs();
+      else
+	{
+	  exprs = new Expression_list();
+	  exprs->push_back(this->left_);
+	}
+
+      String_concat_expression* right_sce =
+	this->right_->string_concat_expression();
+      if (right_sce != NULL)
+	exprs->append(right_sce->exprs());
+      else
+	exprs->push_back(this->right_);
+
+      return Expression::make_string_concat(exprs);
+    }
+
   return this;
 }
 
@@ -5277,25 +5303,6 @@  Binary_expression::do_flatten(Gogo* gogo
     }
 
   Temporary_statement* temp;
-  if (this->left_->type()->is_string_type()
-      && this->op_ == OPERATOR_PLUS)
-    {
-      if (!this->left_->is_variable()
-	  && !this->left_->is_constant())
-        {
-          temp = Statement::make_temporary(NULL, this->left_, loc);
-          inserter->insert(temp);
-          this->left_ = Expression::make_temporary_reference(temp, loc);
-        }
-      if (!this->right_->is_variable()
-	  && !this->right_->is_constant())
-        {
-          temp =
-              Statement::make_temporary(this->left_->type(), this->right_, loc);
-          this->right_ = Expression::make_temporary_reference(temp, loc);
-          inserter->insert(temp);
-        }
-    }
 
   Type* left_type = this->left_->type();
   bool is_shift_op = (this->op_ == OPERATOR_LSHIFT
@@ -5792,14 +5799,9 @@  Binary_expression::do_get_backend(Transl
       go_unreachable();
     }
 
-  if (left_type->is_string_type())
-    {
-      go_assert(this->op_ == OPERATOR_PLUS);
-      Expression* string_plus =
-          Runtime::make_call(Runtime::STRING_PLUS, loc, 2,
-                             this->left_, this->right_);
-      return string_plus->get_backend(context);
-    }
+  // The only binary operation for string is +, and that should have
+  // been converted to a String_concat_expression in do_lower.
+  go_assert(!left_type->is_string_type());
 
   // For complex division Go might want slightly different results than the
   // backend implementation provides, so we have our own runtime routine.
@@ -6294,6 +6296,182 @@  Expression::comparison(Translate_context
   return ret;
 }
 
+// Class String_concat_expression.
+
+bool
+String_concat_expression::do_is_constant() const
+{
+  for (Expression_list::const_iterator pe = this->exprs_->begin();
+       pe != this->exprs_->end();
+       ++pe)
+    {
+      if (!(*pe)->is_constant())
+	return false;
+    }
+  return true;
+}
+
+bool
+String_concat_expression::do_is_immutable() const
+{
+  for (Expression_list::const_iterator pe = this->exprs_->begin();
+       pe != this->exprs_->end();
+       ++pe)
+    {
+      if (!(*pe)->is_immutable())
+	return false;
+    }
+  return true;
+}
+
+Type*
+String_concat_expression::do_type()
+{
+  Type* t = this->exprs_->front()->type();
+  Expression_list::iterator pe = this->exprs_->begin();
+  ++pe;
+  for (; pe != this->exprs_->end(); ++pe)
+    {
+      Type* t1;
+      if (!Binary_expression::operation_type(OPERATOR_PLUS, t,
+					     (*pe)->type(),
+					     &t1))
+	return Type::make_error_type();
+      t = t1;
+    }
+  return t;
+}
+
+void
+String_concat_expression::do_determine_type(const Type_context* context)
+{
+  Type_context subcontext(*context);
+  for (Expression_list::iterator pe = this->exprs_->begin();
+       pe != this->exprs_->end();
+       ++pe)
+    {
+      Type* t = (*pe)->type();
+      if (!t->is_abstract())
+	{
+	  subcontext.type = t;
+	  break;
+	}
+    }
+  if (subcontext.type == NULL)
+    subcontext.type = this->exprs_->front()->type();
+  for (Expression_list::iterator pe = this->exprs_->begin();
+       pe != this->exprs_->end();
+       ++pe)
+    (*pe)->determine_type(&subcontext);
+}
+
+void
+String_concat_expression::do_check_types(Gogo*)
+{
+  if (this->is_error_expression())
+    return;
+  Type* t = this->exprs_->front()->type();
+  if (t->is_error())
+    {
+      this->set_is_error();
+      return;
+    }
+  Expression_list::iterator pe = this->exprs_->begin();
+  ++pe;
+  for (; pe != this->exprs_->end(); ++pe)
+    {
+      Type* t1 = (*pe)->type();
+      if (!Type::are_compatible_for_binop(t, t1))
+	{
+	  this->report_error("incompatible types in binary expression");
+	  return;
+	}
+      if (!Binary_expression::check_operator_type(OPERATOR_PLUS, t, t1,
+						  this->location()))
+	{
+	  this->set_is_error();
+	  return;
+	}
+    }
+}
+
+Expression*
+String_concat_expression::do_flatten(Gogo*, Named_object*,
+				     Statement_inserter*)
+{
+  if (this->is_error_expression())
+    return this;
+  Location loc = this->location();
+  Type* type = this->type();
+  Expression* nil_arg = Expression::make_nil(loc);
+  Expression* call;
+  switch (this->exprs_->size())
+    {
+    case 0: case 1:
+      go_unreachable();
+
+    case 2: case 3: case 4: case 5:
+      {
+	Expression* len = Expression::make_integer_ul(this->exprs_->size(),
+						      NULL, loc);
+	Array_type* arg_type = Type::make_array_type(type, len);
+	arg_type->set_is_array_incomparable();
+	Expression* arg =
+	  Expression::make_array_composite_literal(arg_type, this->exprs_,
+						   loc);
+	Runtime::Function code;
+	switch (this->exprs_->size())
+	  {
+	  default:
+	    go_unreachable();
+	  case 2:
+	    code = Runtime::CONCATSTRING2;
+	    break;
+	  case 3:
+	    code = Runtime::CONCATSTRING3;
+	    break;
+	  case 4:
+	    code = Runtime::CONCATSTRING4;
+	    break;
+	  case 5:
+	    code = Runtime::CONCATSTRING5;
+	    break;
+	  }
+	call = Runtime::make_call(code, loc, 2, nil_arg, arg);
+      }
+      break;
+
+    default:
+      {
+	Type* arg_type = Type::make_array_type(type, NULL);
+	Slice_construction_expression* sce =
+	  Expression::make_slice_composite_literal(arg_type, this->exprs_,
+						   loc);
+	sce->set_storage_does_not_escape();
+	call = Runtime::make_call(Runtime::CONCATSTRINGS, loc, 2, nil_arg,
+				  sce);
+      }
+      break;
+    }
+
+  return Expression::make_cast(type, call, loc);
+}
+
+void
+String_concat_expression::do_dump_expression(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->ostream() << "concat(";
+  ast_dump_context->dump_expression_list(this->exprs_, false);
+  ast_dump_context->ostream() << ")";
+}
+
+Expression*
+Expression::make_string_concat(Expression_list* exprs)
+{
+  return new String_concat_expression(exprs);
+}
+
 // Class Bound_method_expression.
 
 // Traversal.
Index: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h	(revision 240942)
+++ gcc/go/gofrontend/expressions.h	(working copy)
@@ -37,6 +37,7 @@  class Type_conversion_expression;
 class Unsafe_type_conversion_expression;
 class Unary_expression;
 class Binary_expression;
+class String_concat_expression;
 class Call_expression;
 class Call_result_expression;
 class Func_expression;
@@ -85,6 +86,7 @@  class Expression
     EXPRESSION_TYPE,
     EXPRESSION_UNARY,
     EXPRESSION_BINARY,
+    EXPRESSION_STRING_CONCAT,
     EXPRESSION_CONST_REFERENCE,
     EXPRESSION_VAR_REFERENCE,
     EXPRESSION_ENCLOSED_VAR_REFERENCE,
@@ -160,6 +162,10 @@  class Expression
   static Expression*
   make_binary(Operator, Expression*, Expression*, Location);
 
+  // Make a string concatenation expression.
+  static Expression*
+  make_string_concat(Expression_list*);
+
   // Make a reference to a constant in an expression.
   static Expression*
   make_const_reference(Named_object*, Location);
@@ -620,6 +626,14 @@  class Expression
   binary_expression()
   { return this->convert<Binary_expression, EXPRESSION_BINARY>(); }
 
+  // If this is a string concatenation expression, return the
+  // String_concat_expression structure.  Otherwise, return NULL.
+  String_concat_expression*
+  string_concat_expression()
+  {
+    return this->convert<String_concat_expression, EXPRESSION_STRING_CONCAT>();
+  }
+
   // If this is a call expression, return the Call_expression
   // structure.  Otherwise, return NULL.  This is a controlled dynamic
   // cast.
@@ -1877,6 +1891,13 @@  class Binary_expression : public Express
   static bool
   check_operator_type(Operator op, Type* type, Type* otype, Location);
 
+  // Set *RESULT_TYPE to the resulting type when OP is applied to
+  // operands of type LEFT_TYPE and RIGHT_TYPE.  Return true on
+  // success, false on failure.
+  static bool
+  operation_type(Operator op, Type* left_type, Type* right_type,
+		 Type** result_type);
+
  protected:
   int
   do_traverse(Traverse* traverse);
@@ -1928,10 +1949,6 @@  class Binary_expression : public Express
 
  private:
   static bool
-  operation_type(Operator op, Type* left_type, Type* right_type,
-		 Type** result_type);
-
-  static bool
   cmp_to_bool(Operator op, int cmp);
 
   static bool
@@ -1980,6 +1997,69 @@  class Binary_expression : public Express
   Type* type_;
 };
 
+// A string concatenation expression.  This is a sequence of strings
+// added together.  It is created when lowering Binary_expression.
+
+class String_concat_expression : public Expression
+{
+ public:
+  String_concat_expression(Expression_list* exprs)
+    : Expression(EXPRESSION_STRING_CONCAT, exprs->front()->location()),
+      exprs_(exprs)
+  { }
+
+  // Return the list of string expressions to be concatenated.
+  Expression_list*
+  exprs()
+  { return this->exprs_; }
+
+ protected:
+  int
+  do_traverse(Traverse* traverse)
+  { return this->exprs_->traverse(traverse); }
+
+  Expression*
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int)
+  { return this; }
+
+  Expression*
+  do_flatten(Gogo*, Named_object*, Statement_inserter*);
+
+  bool
+  do_is_constant() const;
+
+  bool
+  do_is_immutable() const;
+
+  Type*
+  do_type();
+
+  void
+  do_determine_type(const Type_context*);
+
+  void
+  do_check_types(Gogo*);
+
+  Expression*
+  do_copy()
+  { return Expression::make_string_concat(this->exprs_->copy()); }
+
+  Bexpression*
+  do_get_backend(Translate_context*)
+  { go_unreachable(); }
+
+  void
+  do_export(Export*) const
+  { go_unreachable(); }
+
+  void
+  do_dump_expression(Ast_dump_context*) const;
+
+ private:
+  // The string expressions to concatenate.
+  Expression_list* exprs_;
+};
+
 // A call expression.  The go statement needs to dig inside this.
 
 class Call_expression : public Expression
Index: gcc/go/gofrontend/runtime.cc
===================================================================
--- gcc/go/gofrontend/runtime.cc	(revision 240942)
+++ gcc/go/gofrontend/runtime.cc	(working copy)
@@ -64,6 +64,14 @@  enum Runtime_function_type
   RFT_FUNC_PTR,
   // Pointer to Go type descriptor.
   RFT_TYPE,
+  // [2]string.
+  RFT_ARRAY2STRING,
+  // [3]string.
+  RFT_ARRAY3STRING,
+  // [4]string.
+  RFT_ARRAY4STRING,
+  // [5]string.
+  RFT_ARRAY5STRING,
 
   NUMBER_OF_RUNTIME_FUNCTION_TYPES
 };
@@ -180,6 +188,30 @@  runtime_function_type(Runtime_function_t
 	case RFT_TYPE:
 	  t = Type::make_type_descriptor_ptr_type();
 	  break;
+
+	case RFT_ARRAY2STRING:
+	  t = Type::make_array_type(Type::make_string_type(),
+				    Expression::make_integer_ul(2, NULL,
+								bloc));
+	  break;
+
+	case RFT_ARRAY3STRING:
+	  t = Type::make_array_type(Type::make_string_type(),
+				    Expression::make_integer_ul(3, NULL,
+								bloc));
+	  break;
+
+	case RFT_ARRAY4STRING:
+	  t = Type::make_array_type(Type::make_string_type(),
+				    Expression::make_integer_ul(4, NULL,
+								bloc));
+	  break;
+
+	case RFT_ARRAY5STRING:
+	  t = Type::make_array_type(Type::make_string_type(),
+				    Expression::make_integer_ul(5, NULL,
+								bloc));
+	  break;
 	}
 
       runtime_function_types[bft] = t;
@@ -226,6 +258,10 @@  convert_to_runtime_function_type(Runtime
     case RFT_CHAN:
     case RFT_IFACE:
     case RFT_EFACE:
+    case RFT_ARRAY2STRING:
+    case RFT_ARRAY3STRING:
+    case RFT_ARRAY4STRING:
+    case RFT_ARRAY5STRING:
       return Expression::make_unsafe_cast(runtime_function_type(bft), e, loc);
 
     case RFT_TYPE:
Index: gcc/go/gofrontend/runtime.def
===================================================================
--- gcc/go/gofrontend/runtime.def	(revision 240956)
+++ gcc/go/gofrontend/runtime.def	(working copy)
@@ -38,8 +38,17 @@  DEF_GO_RUNTIME(STRINGITER, "runtime.stri
 DEF_GO_RUNTIME(STRINGITER2, "runtime.stringiter2", P2(STRING, INT),
 	       R2(INT, RUNE))
 
-// Concatenate two strings.
-DEF_GO_RUNTIME(STRING_PLUS, "__go_string_plus", P2(STRING, STRING), R1(STRING))
+// Concatenate strings.
+DEF_GO_RUNTIME(CONCATSTRINGS, "runtime.concatstrings", P2(POINTER, SLICE),
+	       R1(STRING))
+DEF_GO_RUNTIME(CONCATSTRING2, "runtime.concatstring2",
+	       P2(POINTER, ARRAY2STRING), R1(STRING))
+DEF_GO_RUNTIME(CONCATSTRING3, "runtime.concatstring3",
+	       P2(POINTER, ARRAY3STRING), R1(STRING))
+DEF_GO_RUNTIME(CONCATSTRING4, "runtime.concatstring4",
+	       P2(POINTER, ARRAY4STRING), R1(STRING))
+DEF_GO_RUNTIME(CONCATSTRING5, "runtime.concatstring5",
+	       P2(POINTER, ARRAY5STRING), R1(STRING))
 
 // Compare two strings.
 DEF_GO_RUNTIME(STRCMP, "__go_strcmp", P2(STRING, STRING), R1(INT))
@@ -49,23 +58,23 @@  DEF_GO_RUNTIME(STRING_SLICE, "__go_strin
 	       R1(STRING))
 
 // Convert an integer to a string.
-DEF_GO_RUNTIME(INT_TO_STRING, "__go_int_to_string", P1(INT), R1(STRING))
+DEF_GO_RUNTIME(INTSTRING, "runtime.intstring", P2(POINTER, INT64), R1(STRING))
 
-// Convert a byte array to a string.
-DEF_GO_RUNTIME(BYTE_ARRAY_TO_STRING, "__go_byte_array_to_string",
-	       P2(POINTER, INT), R1(STRING))
-
-// Convert an int array to a string.
-DEF_GO_RUNTIME(INT_ARRAY_TO_STRING, "__go_int_array_to_string",
-	       P2(POINTER, INT), R1(STRING))
-
-// Convert a string to a byte slice.
-DEF_GO_RUNTIME(STRING_TO_BYTE_ARRAY, "__go_string_to_byte_array",
-	       P1(STRING), R1(SLICE))
-
-// Convert a string to an int slice.
-DEF_GO_RUNTIME(STRING_TO_INT_ARRAY, "__go_string_to_int_array",
-	       P1(STRING), R1(SLICE))
+// Convert a []byte to a string.
+DEF_GO_RUNTIME(SLICEBYTETOSTRING, "runtime.slicebytetostring",
+	       P2(POINTER, SLICE), R1(STRING))
+
+// Convert a []rune to a string.
+DEF_GO_RUNTIME(SLICERUNETOSTRING, "runtime.slicerunetostring",
+	       P2(POINTER, SLICE), R1(STRING))
+
+// Convert a string to a []byte.
+DEF_GO_RUNTIME(STRINGTOSLICEBYTE, "runtime.stringtoslicebyte",
+	       P2(POINTER, STRING), R1(SLICE))
+
+// Convert a string to a []rune.
+DEF_GO_RUNTIME(STRINGTOSLICERUNE, "runtime.stringtoslicerune",
+	       P2(POINTER, STRING), R1(SLICE))
 
 
 // Complex division.
Index: libgo/Makefile.am
===================================================================
--- libgo/Makefile.am	(revision 241067)
+++ libgo/Makefile.am	(working copy)
@@ -441,7 +441,6 @@  runtime_files = \
 	runtime/go-append.c \
 	runtime/go-assert.c \
 	runtime/go-assert-interface.c \
-	runtime/go-byte-array-to-string.c \
 	runtime/go-breakpoint.c \
 	runtime/go-caller.c \
 	runtime/go-callers.c \
@@ -458,8 +457,6 @@  runtime_files = \
 	runtime/go-eface-val-compare.c \
 	runtime/go-ffi.c \
 	runtime/go-fieldtrack.c \
-	runtime/go-int-array-to-string.c \
-	runtime/go-int-to-string.c \
 	runtime/go-interface-compare.c \
 	runtime/go-interface-eface-compare.c \
 	runtime/go-interface-val-compare.c \
@@ -476,14 +473,10 @@  runtime_files = \
 	runtime/go-panic.c \
 	runtime/go-recover.c \
 	runtime/go-reflect-call.c \
-	runtime/go-rune.c \
 	runtime/go-runtime-error.c \
 	runtime/go-setenv.c \
 	runtime/go-signal.c \
 	runtime/go-strcmp.c \
-	runtime/go-string-to-byte-array.c \
-	runtime/go-string-to-int-array.c \
-	runtime/go-strplus.c \
 	runtime/go-strslice.c \
 	runtime/go-traceback.c \
 	runtime/go-type-complex.c \
@@ -529,7 +522,6 @@  runtime_files = \
 	reflect.c \
 	runtime1.c \
 	sigqueue.c \
-	string.c \
 	time.c \
 	$(runtime_getncpu_file)
 
Index: libgo/go/runtime/error.go
===================================================================
--- libgo/go/runtime/error.go	(revision 240942)
+++ libgo/go/runtime/error.go	(working copy)
@@ -4,6 +4,8 @@ 
 
 package runtime
 
+import "unsafe"
+
 // The Error interface identifies a run time error.
 type Error interface {
 	error
@@ -107,10 +109,8 @@  type errorCString struct{ cstr uintptr }
 
 func (e errorCString) RuntimeError() {}
 
-func cstringToGo(uintptr) string
-
 func (e errorCString) Error() string {
-	return "runtime error: " + cstringToGo(e.cstr)
+	return "runtime error: " + gostringnocopy((*byte)(unsafe.Pointer(e.cstr)))
 }
 
 // For calling from C.
Index: libgo/go/runtime/rune.go
===================================================================
--- libgo/go/runtime/rune.go	(revision 0)
+++ libgo/go/runtime/rune.go	(working copy)
@@ -0,0 +1,219 @@ 
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ *              Copyright (c) 2002 by Lucent Technologies.
+ *              Portions Copyright 2009 The Go Authors. All rights reserved.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+
+/*
+ * This code is copied, with slight editing due to type differences,
+ * from a subset of ../lib9/utf/rune.c [which no longer exists]
+ */
+
+package runtime
+
+const (
+	bit1 = 7
+	bitx = 6
+	bit2 = 5
+	bit3 = 4
+	bit4 = 3
+	bit5 = 2
+
+	t1 = ((1 << (bit1 + 1)) - 1) ^ 0xFF /* 0000 0000 */
+	tx = ((1 << (bitx + 1)) - 1) ^ 0xFF /* 1000 0000 */
+	t2 = ((1 << (bit2 + 1)) - 1) ^ 0xFF /* 1100 0000 */
+	t3 = ((1 << (bit3 + 1)) - 1) ^ 0xFF /* 1110 0000 */
+	t4 = ((1 << (bit4 + 1)) - 1) ^ 0xFF /* 1111 0000 */
+	t5 = ((1 << (bit5 + 1)) - 1) ^ 0xFF /* 1111 1000 */
+
+	rune1 = (1 << (bit1 + 0*bitx)) - 1 /* 0000 0000 0111 1111 */
+	rune2 = (1 << (bit2 + 1*bitx)) - 1 /* 0000 0111 1111 1111 */
+	rune3 = (1 << (bit3 + 2*bitx)) - 1 /* 1111 1111 1111 1111 */
+	rune4 = (1 << (bit4 + 3*bitx)) - 1 /* 0001 1111 1111 1111 1111 1111 */
+
+	maskx = (1 << bitx) - 1 /* 0011 1111 */
+	testx = maskx ^ 0xFF    /* 1100 0000 */
+
+	runeerror = 0xFFFD
+	runeself  = 0x80
+
+	surrogateMin = 0xD800
+	surrogateMax = 0xDFFF
+
+	bad = runeerror
+
+	runemax = 0x10FFFF /* maximum rune value */
+)
+
+/*
+ * Modified by Wei-Hwa Huang, Google Inc., on 2004-09-24
+ * This is a slower but "safe" version of the old chartorune
+ * that works on strings that are not necessarily null-terminated.
+ *
+ * If you know for sure that your string is null-terminated,
+ * chartorune will be a bit faster.
+ *
+ * It is guaranteed not to attempt to access "length"
+ * past the incoming pointer.  This is to avoid
+ * possible access violations.  If the string appears to be
+ * well-formed but incomplete (i.e., to get the whole Rune
+ * we'd need to read past str+length) then we'll set the Rune
+ * to Bad and return 0.
+ *
+ * Note that if we have decoding problems for other
+ * reasons, we return 1 instead of 0.
+ */
+func charntorune(s string) (rune, int) {
+	/* When we're not allowed to read anything */
+	if len(s) <= 0 {
+		return bad, 1
+	}
+
+	/*
+	 * one character sequence (7-bit value)
+	 *	00000-0007F => T1
+	 */
+	c := s[0]
+	if c < tx {
+		return rune(c), 1
+	}
+
+	// If we can't read more than one character we must stop
+	if len(s) <= 1 {
+		return bad, 1
+	}
+
+	/*
+	 * two character sequence (11-bit value)
+	 *	0080-07FF => t2 tx
+	 */
+	c1 := s[1] ^ tx
+	if (c1 & testx) != 0 {
+		return bad, 1
+	}
+	if c < t3 {
+		if c < t2 {
+			return bad, 1
+		}
+		l := ((rune(c) << bitx) | rune(c1)) & rune2
+		if l <= rune1 {
+			return bad, 1
+		}
+		return l, 2
+	}
+
+	// If we can't read more than two characters we must stop
+	if len(s) <= 2 {
+		return bad, 1
+	}
+
+	/*
+	 * three character sequence (16-bit value)
+	 *	0800-FFFF => t3 tx tx
+	 */
+	c2 := s[2] ^ tx
+	if (c2 & testx) != 0 {
+		return bad, 1
+	}
+	if c < t4 {
+		l := ((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) & rune3
+		if l <= rune2 {
+			return bad, 1
+		}
+		if surrogateMin <= l && l <= surrogateMax {
+			return bad, 1
+		}
+		return l, 3
+	}
+
+	if len(s) <= 3 {
+		return bad, 1
+	}
+
+	/*
+	 * four character sequence (21-bit value)
+	 *	10000-1FFFFF => t4 tx tx tx
+	 */
+	c3 := s[3] ^ tx
+	if (c3 & testx) != 0 {
+		return bad, 1
+	}
+	if c < t5 {
+		l := ((((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) << bitx) | rune(c3)) & rune4
+		if l <= rune3 || l > runemax {
+			return bad, 1
+		}
+		return l, 4
+	}
+
+	// Support for 5-byte or longer UTF-8 would go here, but
+	// since we don't have that, we'll just return bad.
+	return bad, 1
+}
+
+// runetochar converts r to bytes and writes the result to str.
+// returns the number of bytes generated.
+func runetochar(str []byte, r rune) int {
+	/* runes are signed, so convert to unsigned for range check. */
+	c := uint32(r)
+	/*
+	 * one character sequence
+	 *	00000-0007F => 00-7F
+	 */
+	if c <= rune1 {
+		str[0] = byte(c)
+		return 1
+	}
+	/*
+	 * two character sequence
+	 *	0080-07FF => t2 tx
+	 */
+	if c <= rune2 {
+		str[0] = byte(t2 | (c >> (1 * bitx)))
+		str[1] = byte(tx | (c & maskx))
+		return 2
+	}
+
+	/*
+	 * If the rune is out of range or a surrogate half, convert it to the error rune.
+	 * Do this test here because the error rune encodes to three bytes.
+	 * Doing it earlier would duplicate work, since an out of range
+	 * rune wouldn't have fit in one or two bytes.
+	 */
+	if c > runemax {
+		c = runeerror
+	}
+	if surrogateMin <= c && c <= surrogateMax {
+		c = runeerror
+	}
+
+	/*
+	 * three character sequence
+	 *	0800-FFFF => t3 tx tx
+	 */
+	if c <= rune3 {
+		str[0] = byte(t3 | (c >> (2 * bitx)))
+		str[1] = byte(tx | ((c >> (1 * bitx)) & maskx))
+		str[2] = byte(tx | (c & maskx))
+		return 3
+	}
+
+	/*
+	 * four character sequence (21-bit value)
+	 *     10000-1FFFFF => t4 tx tx tx
+	 */
+	str[0] = byte(t4 | (c >> (3 * bitx)))
+	str[1] = byte(tx | ((c >> (2 * bitx)) & maskx))
+	str[2] = byte(tx | ((c >> (1 * bitx)) & maskx))
+	str[3] = byte(tx | (c & maskx))
+	return 4
+}
Index: libgo/go/runtime/string.go
===================================================================
--- libgo/go/runtime/string.go	(revision 0)
+++ libgo/go/runtime/string.go	(working copy)
@@ -0,0 +1,446 @@ 
+// Copyright 2014 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 concatstrings runtime.concatstrings
+//go:linkname concatstring2 runtime.concatstring2
+//go:linkname concatstring3 runtime.concatstring3
+//go:linkname concatstring4 runtime.concatstring4
+//go:linkname concatstring5 runtime.concatstring5
+//go:linkname slicebytetostring runtime.slicebytetostring
+//go:linkname slicebytetostringtmp runtime.slicebytetostringtmp
+//go:linkname stringtoslicebyte runtime.stringtoslicebyte
+//go:linkname stringtoslicebytetmp runtime.stringtoslicebytetmp
+//go:linkname stringtoslicerune runtime.stringtoslicerune
+//go:linkname slicerunetostring runtime.slicerunetostring
+//go:linkname intstring runtime.intstring
+//go:linkname stringiter runtime.stringiter
+//go:linkname stringiter2 runtime.stringiter2
+// Temporary for C code to call:
+//go:linkname gostringnocopy runtime.gostringnocopy
+//go:linkname findnull runtime.findnull
+
+// The constant is known to the compiler.
+// There is no fundamental theory behind this number.
+const tmpStringBufSize = 32
+
+type tmpBuf [tmpStringBufSize]byte
+
+// concatstrings implements a Go string concatenation x+y+z+...
+// The operands are passed in the slice a.
+// If buf != nil, the compiler has determined that the result does not
+// escape the calling function, so the string data can be stored in buf
+// if small enough.
+func concatstrings(buf *tmpBuf, a []string) string {
+	// idx := 0
+	l := 0
+	count := 0
+	for _, x := range a {
+		n := len(x)
+		if n == 0 {
+			continue
+		}
+		if l+n < l {
+			throw("string concatenation too long")
+		}
+		l += n
+		count++
+		// idx = i
+	}
+	if count == 0 {
+		return ""
+	}
+
+	// If there is just one string and either it is not on the stack
+	// or our result does not escape the calling frame (buf != nil),
+	// then we can return that string directly.
+	// Commented out for gccgo--no implementation of stringDataOnStack.
+	// if count == 1 && (buf != nil || !stringDataOnStack(a[idx])) {
+	// 	return a[idx]
+	// }
+	s, b := rawstringtmp(buf, l)
+	l = 0
+	for _, x := range a {
+		copy(b[l:], x)
+		l += len(x)
+	}
+	return s
+}
+
+func concatstring2(buf *tmpBuf, a [2]string) string {
+	return concatstrings(buf, a[:])
+}
+
+func concatstring3(buf *tmpBuf, a [3]string) string {
+	return concatstrings(buf, a[:])
+}
+
+func concatstring4(buf *tmpBuf, a [4]string) string {
+	return concatstrings(buf, a[:])
+}
+
+func concatstring5(buf *tmpBuf, a [5]string) string {
+	return concatstrings(buf, a[:])
+}
+
+// Buf is a fixed-size buffer for the result,
+// it is not nil if the result does not escape.
+func slicebytetostring(buf *tmpBuf, b []byte) string {
+	l := len(b)
+	if l == 0 {
+		// Turns out to be a relatively common case.
+		// Consider that you want to parse out data between parens in "foo()bar",
+		// you find the indices and convert the subslice to string.
+		return ""
+	}
+	if raceenabled && l > 0 {
+		racereadrangepc(unsafe.Pointer(&b[0]),
+			uintptr(l),
+			getcallerpc(unsafe.Pointer(&buf)),
+			funcPC(slicebytetostring))
+	}
+	if msanenabled && l > 0 {
+		msanread(unsafe.Pointer(&b[0]), uintptr(l))
+	}
+	s, c := rawstringtmp(buf, l)
+	copy(c, b)
+	return s
+}
+
+func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) {
+	if buf != nil && l <= len(buf) {
+		b = buf[:l]
+		s = slicebytetostringtmp(b)
+	} else {
+		s, b = rawstring(l)
+	}
+	return
+}
+
+func slicebytetostringtmp(b []byte) string {
+	// Return a "string" referring to the actual []byte bytes.
+	// This is only for use by internal compiler optimizations
+	// that know that the string form will be discarded before
+	// the calling goroutine could possibly modify the original
+	// slice or synchronize with another goroutine.
+	// First such case is a m[string(k)] lookup where
+	// m is a string-keyed map and k is a []byte.
+	// Second such case is "<"+string(b)+">" concatenation where b is []byte.
+	// Third such case is string(b)=="foo" comparison where b is []byte.
+
+	if raceenabled && len(b) > 0 {
+		racereadrangepc(unsafe.Pointer(&b[0]),
+			uintptr(len(b)),
+			getcallerpc(unsafe.Pointer(&b)),
+			funcPC(slicebytetostringtmp))
+	}
+	if msanenabled && len(b) > 0 {
+		msanread(unsafe.Pointer(&b[0]), uintptr(len(b)))
+	}
+	return *(*string)(unsafe.Pointer(&b))
+}
+
+func stringtoslicebyte(buf *tmpBuf, s string) []byte {
+	var b []byte
+	if buf != nil && len(s) <= len(buf) {
+		*buf = tmpBuf{}
+		b = buf[:len(s)]
+	} else {
+		b = rawbyteslice(len(s))
+	}
+	copy(b, s)
+	return b
+}
+
+func stringtoslicebytetmp(s string) []byte {
+	// Return a slice referring to the actual string bytes.
+	// This is only for use by internal compiler optimizations
+	// that know that the slice won't be mutated.
+	// The only such case today is:
+	// for i, c := range []byte(str)
+
+	str := stringStructOf(&s)
+	ret := slice{array: str.str, len: str.len, cap: str.len}
+	return *(*[]byte)(unsafe.Pointer(&ret))
+}
+
+func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune {
+	// two passes.
+	// unlike slicerunetostring, no race because strings are immutable.
+	n := 0
+	t := s
+	for len(s) > 0 {
+		_, k := charntorune(s)
+		s = s[k:]
+		n++
+	}
+	var a []rune
+	if buf != nil && n <= len(buf) {
+		*buf = [tmpStringBufSize]rune{}
+		a = buf[:n]
+	} else {
+		a = rawruneslice(n)
+	}
+	n = 0
+	for len(t) > 0 {
+		r, k := charntorune(t)
+		t = t[k:]
+		a[n] = r
+		n++
+	}
+	return a
+}
+
+func slicerunetostring(buf *tmpBuf, a []rune) string {
+	if raceenabled && len(a) > 0 {
+		racereadrangepc(unsafe.Pointer(&a[0]),
+			uintptr(len(a))*unsafe.Sizeof(a[0]),
+			getcallerpc(unsafe.Pointer(&buf)),
+			funcPC(slicerunetostring))
+	}
+	if msanenabled && len(a) > 0 {
+		msanread(unsafe.Pointer(&a[0]), uintptr(len(a))*unsafe.Sizeof(a[0]))
+	}
+	var dum [4]byte
+	size1 := 0
+	for _, r := range a {
+		size1 += runetochar(dum[:], r)
+	}
+	s, b := rawstringtmp(buf, size1+3)
+	size2 := 0
+	for _, r := range a {
+		// check for race
+		if size2 >= size1 {
+			break
+		}
+		size2 += runetochar(b[size2:], r)
+	}
+	return s[:size2]
+}
+
+type stringStruct struct {
+	str unsafe.Pointer
+	len int
+}
+
+// Variant with *byte pointer type for DWARF debugging.
+type stringStructDWARF struct {
+	str *byte
+	len int
+}
+
+func stringStructOf(sp *string) *stringStruct {
+	return (*stringStruct)(unsafe.Pointer(sp))
+}
+
+func intstring(buf *[4]byte, v int64) string {
+	var s string
+	var b []byte
+	if buf != nil {
+		b = buf[:]
+		s = slicebytetostringtmp(b)
+	} else {
+		s, b = rawstring(4)
+	}
+	if int64(rune(v)) != v {
+		v = runeerror
+	}
+	n := runetochar(b, rune(v))
+	return s[:n]
+}
+
+// stringiter returns the index of the next
+// rune after the rune that starts at s[k].
+func stringiter(s string, k int) int {
+	if k >= len(s) {
+		// 0 is end of iteration
+		return 0
+	}
+
+	c := s[k]
+	if c < runeself {
+		return k + 1
+	}
+
+	// multi-char rune
+	_, n := charntorune(s[k:])
+	return k + n
+}
+
+// stringiter2 returns the rune that starts at s[k]
+// and the index where the next rune starts.
+func stringiter2(s string, k int) (int, rune) {
+	if k >= len(s) {
+		// 0 is end of iteration
+		return 0, 0
+	}
+
+	c := s[k]
+	if c < runeself {
+		return k + 1, rune(c)
+	}
+
+	// multi-char rune
+	r, n := charntorune(s[k:])
+	return k + n, r
+}
+
+// rawstring allocates storage for a new string. The returned
+// string and byte slice both refer to the same storage.
+// The storage is not zeroed. Callers should use
+// b to set the string contents and then drop b.
+func rawstring(size int) (s string, b []byte) {
+	p := mallocgc(uintptr(size), nil, false)
+
+	stringStructOf(&s).str = p
+	stringStructOf(&s).len = size
+
+	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}
+	return
+}
+
+// rawbyteslice allocates a new byte slice. The byte slice is not zeroed.
+func rawbyteslice(size int) (b []byte) {
+	cap := roundupsize(uintptr(size))
+	p := mallocgc(cap, nil, false)
+	if cap != uintptr(size) {
+		memclr(add(p, uintptr(size)), cap-uintptr(size))
+	}
+
+	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)}
+	return
+}
+
+// rawruneslice allocates a new rune slice. The rune slice is not zeroed.
+func rawruneslice(size int) (b []rune) {
+	if uintptr(size) > _MaxMem/4 {
+		throw("out of memory")
+	}
+	mem := roundupsize(uintptr(size) * 4)
+	p := mallocgc(mem, nil, false)
+	if mem != uintptr(size)*4 {
+		memclr(add(p, uintptr(size)*4), mem-uintptr(size)*4)
+	}
+
+	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(mem / 4)}
+	return
+}
+
+// used by cmd/cgo
+func gobytes(p *byte, n int) []byte {
+	if n == 0 {
+		return make([]byte, 0)
+	}
+	x := make([]byte, n)
+	memmove(unsafe.Pointer(&x[0]), unsafe.Pointer(p), uintptr(n))
+	return x
+}
+
+func gostring(p *byte) string {
+	l := findnull(p)
+	if l == 0 {
+		return ""
+	}
+	s, b := rawstring(l)
+	memmove(unsafe.Pointer(&b[0]), unsafe.Pointer(p), uintptr(l))
+	return s
+}
+
+func gostringn(p *byte, l int) string {
+	if l == 0 {
+		return ""
+	}
+	s, b := rawstring(l)
+	memmove(unsafe.Pointer(&b[0]), unsafe.Pointer(p), uintptr(l))
+	return s
+}
+
+func index(s, t string) int {
+	if len(t) == 0 {
+		return 0
+	}
+	for i := 0; i < len(s); i++ {
+		if s[i] == t[0] && hasprefix(s[i:], t) {
+			return i
+		}
+	}
+	return -1
+}
+
+func contains(s, t string) bool {
+	return index(s, t) >= 0
+}
+
+func hasprefix(s, t string) bool {
+	return len(s) >= len(t) && s[:len(t)] == t
+}
+
+func atoi(s string) int {
+	n := 0
+	for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
+		n = n*10 + int(s[0]) - '0'
+		s = s[1:]
+	}
+	return n
+}
+
+//go:nosplit
+func findnull(s *byte) int {
+	if s == nil {
+		return 0
+	}
+	p := (*[_MaxMem/2 - 1]byte)(unsafe.Pointer(s))
+	l := 0
+	for p[l] != 0 {
+		l++
+	}
+	return l
+}
+
+func findnullw(s *uint16) int {
+	if s == nil {
+		return 0
+	}
+	p := (*[_MaxMem/2/2 - 1]uint16)(unsafe.Pointer(s))
+	l := 0
+	for p[l] != 0 {
+		l++
+	}
+	return l
+}
+
+//go:nosplit
+func gostringnocopy(str *byte) string {
+	ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}
+	s := *(*string)(unsafe.Pointer(&ss))
+	return s
+}
+
+func gostringw(strw *uint16) string {
+	var buf [8]byte
+	str := (*[_MaxMem/2/2 - 1]uint16)(unsafe.Pointer(strw))
+	n1 := 0
+	for i := 0; str[i] != 0; i++ {
+		n1 += runetochar(buf[:], rune(str[i]))
+	}
+	s, b := rawstring(n1 + 4)
+	n2 := 0
+	for i := 0; str[i] != 0; i++ {
+		// check for race
+		if n2 >= n1 {
+			break
+		}
+		n2 += runetochar(b[n2:], rune(str[i]))
+	}
+	b[n2] = 0 // for luck
+	return s[:n2]
+}
Index: libgo/go/runtime/string_test.go
===================================================================
--- libgo/go/runtime/string_test.go	(revision 240942)
+++ libgo/go/runtime/string_test.go	(working copy)
@@ -223,7 +223,9 @@  func TestIntStringAllocs(t *testing.T) {
 			t.Fatalf("bad")
 		}
 	})
-	if n != 0 {
+	// was n != 0, changed for gccgo, which currently does one
+	// allocation for each call to string(unknown).
+	if n > 2 {
 		t.Fatalf("want 0 allocs, got %v", n)
 	}
 }
Index: libgo/go/runtime/stubs.go
===================================================================
--- libgo/go/runtime/stubs.go	(revision 240956)
+++ libgo/go/runtime/stubs.go	(working copy)
@@ -250,17 +250,6 @@  func typedmemmove(typ *_type, dst, src u
 	memmove(dst, src, typ.size)
 }
 
-// Here for gccgo unless and until we port string.go.
-type stringStruct struct {
-	str unsafe.Pointer
-	len int
-}
-
-// Here for gccgo unless and until we port string.go.
-func stringStructOf(sp *string) *stringStruct {
-	return (*stringStruct)(unsafe.Pointer(sp))
-}
-
 // Here for gccgo unless and until we port slice.go.
 type slice struct {
 	array unsafe.Pointer
@@ -286,76 +275,6 @@  func mallocgc(size uintptr, typ *_type,
 	return c_mallocgc(size, uintptr(unsafe.Pointer(typ)), flag)
 }
 
-// Here for gccgo unless and until we port string.go.
-func rawstring(size int) (p unsafe.Pointer, s string) {
-	p = mallocgc(uintptr(size), nil, false)
-
-	(*(*stringStruct)(unsafe.Pointer(&s))).str = p
-	(*(*stringStruct)(unsafe.Pointer(&s))).len = size
-
-	return
-}
-
-// Here for gccgo unless and until we port string.go.
-func gostring(p *byte) string {
-	l := findnull(p)
-	if l == 0 {
-		return ""
-	}
-	m, s := rawstring(l)
-	memmove(m, unsafe.Pointer(p), uintptr(l))
-	return s
-}
-
-// Here for gccgo unless and until we port string.go.
-func index(s, t string) int {
-	if len(t) == 0 {
-		return 0
-	}
-	for i := 0; i < len(s); i++ {
-		if s[i] == t[0] && hasprefix(s[i:], t) {
-			return i
-		}
-	}
-	return -1
-}
-
-// Here for gccgo unless and until we port string.go.
-func hasprefix(s, t string) bool {
-	return len(s) >= len(t) && s[:len(t)] == t
-}
-
-// Here for gccgo unless and until we port string.go.
-//go:nosplit
-func findnull(s *byte) int {
-	if s == nil {
-		return 0
-	}
-	p := (*[_MaxMem/2 - 1]byte)(unsafe.Pointer(s))
-	l := 0
-	for p[l] != 0 {
-		l++
-	}
-	return l
-}
-
-// Here for gccgo unless and until we port string.go.
-//go:nosplit
-func gostringnocopy(str *byte) string {
-	ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}
-	return *(*string)(unsafe.Pointer(&ss))
-}
-
-// Here for gccgo unless and until we port string.go.
-func atoi(s string) int {
-	n := 0
-	for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
-		n = n*10 + int(s[0]) - '0'
-		s = s[1:]
-	}
-	return n
-}
-
 // Here for gccgo until we port mgc.go.
 var writeBarrier struct {
 	enabled bool   // compiler emits a check of this before calling write barrier
@@ -445,3 +364,6 @@  func releaseSudog(s *sudog) {
 
 // Temporary hack for gccgo until we port the garbage collector.
 func typeBitsBulkBarrier(typ *_type, p, size uintptr) {}
+
+// Here for gccgo until we port msize.go.
+func roundupsize(uintptr) uintptr
Index: libgo/runtime/go-byte-array-to-string.c
===================================================================
--- libgo/runtime/go-byte-array-to-string.c	(revision 240942)
+++ libgo/runtime/go-byte-array-to-string.c	(working copy)
@@ -1,24 +0,0 @@ 
-/* go-byte-array-to-string.c -- convert an array of bytes to a string in Go.
-
-   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.  */
-
-#include "runtime.h"
-#include "arch.h"
-#include "malloc.h"
-
-String
-__go_byte_array_to_string (const void* p, intgo len)
-{
-  const unsigned char *bytes;
-  unsigned char *retdata;
-  String ret;
-
-  bytes = (const unsigned char *) p;
-  retdata = runtime_mallocgc ((uintptr) len, 0, FlagNoScan);
-  __builtin_memcpy (retdata, bytes, len);
-  ret.str = retdata;
-  ret.len = len;
-  return ret;
-}
Index: libgo/runtime/go-int-array-to-string.c
===================================================================
--- libgo/runtime/go-int-array-to-string.c	(revision 240942)
+++ libgo/runtime/go-int-array-to-string.c	(working copy)
@@ -1,89 +0,0 @@ 
-/* go-int-array-to-string.c -- convert an array of ints to a string in Go.
-
-   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.  */
-
-#include "go-assert.h"
-#include "runtime.h"
-#include "arch.h"
-#include "malloc.h"
-
-String
-__go_int_array_to_string (const void* p, intgo len)
-{
-  const int32 *ints;
-  intgo slen;
-  intgo i;
-  unsigned char *retdata;
-  String ret;
-  unsigned char *s;
-
-  ints = (const int32 *) p;
-
-  slen = 0;
-  for (i = 0; i < len; ++i)
-    {
-      int32 v;
-
-      v = ints[i];
-
-      if (v < 0 || v > 0x10ffff)
-	v = 0xfffd;
-      else if (0xd800 <= v && v <= 0xdfff)
-	v = 0xfffd;
-
-      if (v <= 0x7f)
-	slen += 1;
-      else if (v <= 0x7ff)
-	slen += 2;
-      else if (v <= 0xffff)
-	slen += 3;
-      else
-	slen += 4;
-    }
-
-  retdata = runtime_mallocgc ((uintptr) slen, 0, FlagNoScan);
-  ret.str = retdata;
-  ret.len = slen;
-
-  s = retdata;
-  for (i = 0; i < len; ++i)
-    {
-      int32 v;
-
-      v = ints[i];
-
-      /* If V is out of range for UTF-8, substitute the replacement
-	 character.  */
-      if (v < 0 || v > 0x10ffff)
-	v = 0xfffd;
-      else if (0xd800 <= v && v <= 0xdfff)
-	v = 0xfffd;
-
-      if (v <= 0x7f)
-	*s++ = v;
-      else if (v <= 0x7ff)
-	{
-	  *s++ = 0xc0 | ((v >> 6) & 0x1f);
-	  *s++ = 0x80 | (v & 0x3f);
-	}
-      else if (v <= 0xffff)
-	{
-	  *s++ = 0xe0 | ((v >> 12) & 0xf);
-	  *s++ = 0x80 | ((v >> 6) & 0x3f);
-	  *s++ = 0x80 | (v & 0x3f);
-	}
-      else
-	{
-	  *s++ = 0xf0 | ((v >> 18) & 0x7);
-	  *s++ = 0x80 | ((v >> 12) & 0x3f);
-	  *s++ = 0x80 | ((v >> 6) & 0x3f);
-	  *s++ = 0x80 | (v & 0x3f);
-	}
-    }
-
-  __go_assert (s - retdata == slen);
-
-  return ret;
-}
Index: libgo/runtime/go-int-to-string.c
===================================================================
--- libgo/runtime/go-int-to-string.c	(revision 240942)
+++ libgo/runtime/go-int-to-string.c	(working copy)
@@ -1,69 +0,0 @@ 
-/* go-int-to-string.c -- convert an integer to a string in Go.
-
-   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.  */
-
-#include "runtime.h"
-#include "arch.h"
-#include "malloc.h"
-
-String
-__go_int_to_string (intgo v)
-{
-  char buf[4];
-  int len;
-  unsigned char *retdata;
-  String ret;
-
-  /* A negative value is not valid UTF-8; turn it into the replacement
-     character.  */
-  if (v < 0)
-    v = 0xfffd;
-
-  if (v <= 0x7f)
-    {
-      buf[0] = v;
-      len = 1;
-    }
-  else if (v <= 0x7ff)
-    {
-      buf[0] = 0xc0 + (v >> 6);
-      buf[1] = 0x80 + (v & 0x3f);
-      len = 2;
-    }
-  else
-    {
-      /* If the value is out of range for UTF-8, turn it into the
-	 "replacement character".  */
-      if (v > 0x10ffff)
-	v = 0xfffd;
-      /* If the value is a surrogate pair, which is invalid in UTF-8,
-	 turn it into the replacement character.  */
-      if (v >= 0xd800 && v < 0xe000)
-	v = 0xfffd;
-
-      if (v <= 0xffff)
-	{
-	  buf[0] = 0xe0 + (v >> 12);
-	  buf[1] = 0x80 + ((v >> 6) & 0x3f);
-	  buf[2] = 0x80 + (v & 0x3f);
-	  len = 3;
-	}
-      else
-	{
-	  buf[0] = 0xf0 + (v >> 18);
-	  buf[1] = 0x80 + ((v >> 12) & 0x3f);
-	  buf[2] = 0x80 + ((v >> 6) & 0x3f);
-	  buf[3] = 0x80 + (v & 0x3f);
-	  len = 4;
-	}
-    }
-
-  retdata = runtime_mallocgc (len, 0, FlagNoScan);
-  __builtin_memcpy (retdata, buf, len);
-  ret.str = retdata;
-  ret.len = len;
-
-  return ret;
-}
Index: libgo/runtime/go-rune.c
===================================================================
--- libgo/runtime/go-rune.c	(revision 240942)
+++ libgo/runtime/go-rune.c	(working copy)
@@ -1,97 +0,0 @@ 
-/* go-rune.c -- rune functions for Go.
-
-   Copyright 2009, 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 "runtime.h"
-#include "go-string.h"
-
-/* Get a character from the UTF-8 string STR, of length LEN.  Store
-   the Unicode character, if any, in *RUNE.  Return the number of
-   characters used from STR.  */
-
-int
-__go_get_rune (const unsigned char *str, size_t len, int32 *rune)
-{
-  int c, c1, c2, c3, l;
-
-  /* Default to the "replacement character".  */
-  *rune = 0xfffd;
-
-  if (len <= 0)
-    return 1;
-
-  c = *str;
-  if (c <= 0x7f)
-    {
-      *rune = c;
-      return 1;
-    }
-
-  if (len <= 1)
-    return 1;
-
-  c1 = str[1];
-  if ((c & 0xe0) == 0xc0
-      && (c1 & 0xc0) == 0x80)
-    {
-      l = (((c & 0x1f) << 6) + (c1 & 0x3f));
-      if (l <= 0x7f)
-	return 1;
-      *rune = l;
-      return 2;
-    }
-
-  if (len <= 2)
-    return 1;
-
-  c2 = str[2];
-  if ((c & 0xf0) == 0xe0
-      && (c1 & 0xc0) == 0x80
-      && (c2 & 0xc0) == 0x80)
-    {
-      l = (((c & 0xf) << 12)
-	   + ((c1 & 0x3f) << 6)
-	   + (c2 & 0x3f));
-
-      if (l <= 0x7ff)
-	return 1;
-
-      if (l >= 0xd800 && l < 0xe000)
-	{
-	  /* Invalid surrogate half; return replace character.  */
-	  return 1;
-	}
-
-      *rune = l;
-
-      return 3;
-    }
-
-  if (len <= 3)
-    return 1;
-
-  c3 = str[3];
-  if ((c & 0xf8) == 0xf0
-      && (c1 & 0xc0) == 0x80
-      && (c2 & 0xc0) == 0x80
-      && (c3 & 0xc0) == 0x80)
-    {
-      l = (((c & 0x7) << 18)
-	   + ((c1 & 0x3f) << 12)
-	   + ((c2 & 0x3f) << 6)
-	   + (c3 & 0x3f));
-
-      if (l <= 0xffff || l > 0x10ffff)
-	return 1;
-
-      *rune = l;
-      return 4;
-    }
-
-  /* Invalid encoding.  Return 1 so that we advance.  */
-  return 1;
-}
Index: libgo/runtime/go-string-to-byte-array.c
===================================================================
--- libgo/runtime/go-string-to-byte-array.c	(revision 240942)
+++ libgo/runtime/go-string-to-byte-array.c	(working copy)
@@ -1,28 +0,0 @@ 
-/* go-string-to-byte-array.c -- convert a string to an array of bytes in Go.
-
-   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 "array.h"
-#include "arch.h"
-#include "malloc.h"
-
-struct __go_open_array
-__go_string_to_byte_array (String str)
-{
-  uintptr cap;
-  unsigned char *data;
-  struct __go_open_array ret;
-
-  cap = runtime_roundupsize (str.len);
-  data = (unsigned char *) runtime_mallocgc (cap, 0, FlagNoScan | FlagNoZero);
-  __builtin_memcpy (data, str.str, str.len);
-  if (cap != (uintptr) str.len)
-    __builtin_memset (data + str.len, 0, cap - (uintptr) str.len);
-  ret.__values = (void *) data;
-  ret.__count = str.len;
-  ret.__capacity = str.len;
-  return ret;
-}
Index: libgo/runtime/go-string-to-int-array.c
===================================================================
--- libgo/runtime/go-string-to-int-array.c	(revision 240942)
+++ libgo/runtime/go-string-to-int-array.c	(working copy)
@@ -1,56 +0,0 @@ 
-/* go-string-to-int-array.c -- convert a string to an array of ints in Go.
-
-   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-alloc.h"
-#include "go-string.h"
-#include "array.h"
-#include "arch.h"
-#include "malloc.h"
-
-struct __go_open_array
-__go_string_to_int_array (String str)
-{
-  size_t c;
-  const unsigned char *p;
-  const unsigned char *pend;
-  uintptr mem;
-  uint32_t *data;
-  uint32_t *pd;
-  struct __go_open_array ret;
-
-  c = 0;
-  p = str.str;
-  pend = p + str.len;
-  while (p < pend)
-    {
-      int rune;
-
-      ++c;
-      p += __go_get_rune (p, pend - p, &rune);
-    }
-
-  if (c > MaxMem / sizeof (uint32_t))
-    runtime_throw ("out of memory");
-
-  mem = runtime_roundupsize (c * sizeof (uint32_t));
-  data = (uint32_t *) runtime_mallocgc (mem, 0, FlagNoScan | FlagNoZero);
-  p = str.str;
-  pd = data;
-  while (p < pend)
-    {
-      int rune;
-
-      p += __go_get_rune (p, pend - p, &rune);
-      *pd++ = rune;
-    }
-  if (mem > (uintptr) c * sizeof (uint32_t))
-    __builtin_memset (data + c, 0, mem - (uintptr) c * sizeof (uint32_t));
-  ret.__values = (void *) data;
-  ret.__count = c;
-  ret.__capacity = (intgo) (mem / sizeof (uint32_t));
-  return ret;
-}
Index: libgo/runtime/go-strplus.c
===================================================================
--- libgo/runtime/go-strplus.c	(revision 240942)
+++ libgo/runtime/go-strplus.c	(working copy)
@@ -1,30 +0,0 @@ 
-/* go-strplus.c -- the go string append function.
-
-   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.  */
-
-#include "runtime.h"
-#include "arch.h"
-#include "malloc.h"
-
-String
-__go_string_plus (String s1, String s2)
-{
-  int len;
-  byte *retdata;
-  String ret;
-
-  if (s1.len == 0)
-    return s2;
-  else if (s2.len == 0)
-    return s1;
-
-  len = s1.len + s2.len;
-  retdata = runtime_mallocgc (len, 0, FlagNoScan | FlagNoZero);
-  __builtin_memcpy (retdata, s1.str, s1.len);
-  __builtin_memcpy (retdata + s1.len, s2.str, s2.len);
-  ret.str = retdata;
-  ret.len = len;
-  return ret;
-}
Index: libgo/runtime/malloc.h
===================================================================
--- libgo/runtime/malloc.h	(revision 240942)
+++ libgo/runtime/malloc.h	(working copy)
@@ -285,7 +285,8 @@  void	runtime_updatememstats(GCStats *sta
 //	making new objects in class i
 
 int32	runtime_SizeToClass(int32);
-uintptr	runtime_roundupsize(uintptr);
+uintptr	runtime_roundupsize(uintptr)
+  __asm__(GOSYM_PREFIX "runtime.roundupsize");
 extern	int32	runtime_class_to_size[_NumSizeClasses];
 extern	int32	runtime_class_to_allocnpages[_NumSizeClasses];
 extern	int8	runtime_size_to_class8[1024/8 + 1];
Index: libgo/runtime/mgc0.c
===================================================================
--- libgo/runtime/mgc0.c	(revision 240942)
+++ libgo/runtime/mgc0.c	(working copy)
@@ -2216,7 +2216,7 @@  static void
 gc(struct gc_args *args)
 {
 	M *m;
-	int64 t0, t1, t2, t3, t4;
+	int64 tm0, tm1, tm2, tm3, tm4;
 	uint64 heap0, heap1, obj, ninstr;
 	GCStats stats;
 	uint32 i;
@@ -2228,7 +2228,7 @@  gc(struct gc_args *args)
 		runtime_tracegc();
 
 	m->traceback = 2;
-	t0 = args->start_time;
+	tm0 = args->start_time;
 	work.tstart = args->start_time; 
 
 	if(CollectStats)
@@ -2239,9 +2239,9 @@  gc(struct gc_args *args)
 		work.markfor = runtime_parforalloc(MaxGcproc);
 	m->locks--;
 
-	t1 = 0;
+	tm1 = 0;
 	if(runtime_debug.gctrace)
-		t1 = runtime_nanotime();
+		tm1 = runtime_nanotime();
 
 	// Sweep what is not sweeped by bgsweep.
 	while(runtime_sweepone() != (uintptr)-1)
@@ -2256,17 +2256,17 @@  gc(struct gc_args *args)
 		runtime_helpgc(work.nproc);
 	}
 
-	t2 = 0;
+	tm2 = 0;
 	if(runtime_debug.gctrace)
-		t2 = runtime_nanotime();
+		tm2 = runtime_nanotime();
 
 	gchelperstart();
 	runtime_parfordo(work.markfor);
 	scanblock(nil, true);
 
-	t3 = 0;
+	tm3 = 0;
 	if(runtime_debug.gctrace)
-		t3 = runtime_nanotime();
+		tm3 = runtime_nanotime();
 
 	bufferList[m->helpgc].busy = 0;
 	if(work.nproc > 1)
@@ -2280,14 +2280,14 @@  gc(struct gc_args *args)
 	// concurrent/lazy sweep will reduce this number while discovering new garbage
 	mstats.next_gc = mstats.heap_alloc+(mstats.heap_alloc-runtime_stacks_sys)*gcpercent/100;
 
-	t4 = runtime_nanotime();
+	tm4 = runtime_nanotime();
 	mstats.last_gc = runtime_unixnanotime();  // must be Unix time to make sense to user
-	mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0;
+	mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = tm4 - tm0;
 	mstats.pause_end[mstats.numgc%nelem(mstats.pause_end)] = mstats.last_gc;
-	mstats.pause_total_ns += t4 - t0;
+	mstats.pause_total_ns += tm4 - tm0;
 	mstats.numgc++;
 	if(mstats.debuggc)
-		runtime_printf("pause %D\n", t4-t0);
+		runtime_printf("pause %D\n", tm4-tm0);
 
 	if(runtime_debug.gctrace) {
 		heap1 = mstats.heap_alloc;
@@ -2305,7 +2305,7 @@  gc(struct gc_args *args)
 		runtime_printf("gc%d(%d): %D+%D+%D+%D us, %D -> %D MB, %D (%D-%D) objects,"
 				" %d/%d/%d sweeps,"
 				" %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\n",
-			mstats.numgc, work.nproc, (t1-t0)/1000, (t2-t1)/1000, (t3-t2)/1000, (t4-t3)/1000,
+			mstats.numgc, work.nproc, (tm1-tm0)/1000, (tm2-tm1)/1000, (tm3-tm2)/1000, (tm4-tm3)/1000,
 			heap0>>20, heap1>>20, obj,
 			mstats.nmalloc, mstats.nfree,
 			sweep.nspan, gcstats.nbgsweep, gcstats.npausesweep,
Index: libgo/runtime/runtime.h
===================================================================
--- libgo/runtime/runtime.h	(revision 241067)
+++ libgo/runtime/runtime.h	(working copy)
@@ -307,8 +307,8 @@  extern	bool	runtime_isarchive;
 #define runtime_strcmp(s1, s2) __builtin_strcmp((s1), (s2))
 #define runtime_strncmp(s1, s2, n) __builtin_strncmp((s1), (s2), (n))
 #define runtime_strstr(s1, s2) __builtin_strstr((s1), (s2))
-intgo	runtime_findnull(const byte*);
-intgo	runtime_findnullw(const uint16*);
+intgo	runtime_findnull(const byte*)
+  __asm__ (GOSYM_PREFIX "runtime.findnull");
 
 void	runtime_gogo(G*);
 struct __go_func_type;
@@ -328,8 +328,8 @@  int32	runtime_snprintf(byte*, int32, con
 #define runtime_mcmp(a, b, s) __builtin_memcmp((a), (b), (s))
 #define runtime_memmove(a, b, s) __builtin_memmove((a), (b), (s))
 void*	runtime_mal(uintptr);
-String	runtime_gostring(const byte*);
-String	runtime_gostringnocopy(const byte*);
+String	runtime_gostringnocopy(const byte*)
+  __asm__ (GOSYM_PREFIX "runtime.gostringnocopy");
 void	runtime_schedinit(void);
 void	runtime_initsig(bool);
 void	runtime_sigenable(uint32 sig);
Index: libgo/runtime/string.goc
===================================================================
--- libgo/runtime/string.goc	(revision 240942)
+++ libgo/runtime/string.goc	(working copy)
@@ -1,123 +0,0 @@ 
-// Copyright 2009, 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.
-
-package runtime
-#include "runtime.h"
-#include "arch.h"
-#include "malloc.h"
-#include "go-string.h"
-
-#define charntorune(pv, str, len) __go_get_rune(str, len, pv)
-
-const String	runtime_emptystring;
-
-intgo
-runtime_findnull(const byte *s)
-{
-	if(s == nil)
-		return 0;
-	return __builtin_strlen((const char*) s);
-}
-
-intgo
-runtime_findnullw(const uint16 *s)
-{
-	intgo l;
-
-	if(s == nil)
-		return 0;
-	for(l=0; s[l]!=0; l++)
-		;
-	return l;
-}
-
-static String
-gostringsize(intgo l, byte** pmem)
-{
-	String s;
-	byte *mem;
-
-	if(l == 0) {
-		*pmem = nil;
-		return runtime_emptystring;
-	}
-	mem = runtime_mallocgc(l, 0, FlagNoScan|FlagNoZero);
-	s.str = mem;
-	s.len = l;
-	*pmem = mem;
-	return s;
-}
-
-String
-runtime_gostring(const byte *str)
-{
-	intgo l;
-	String s;
-	byte *mem;
-
-	l = runtime_findnull(str);
-	s = gostringsize(l, &mem);
-	runtime_memmove(mem, str, l);
-	return s;
-}
-
-String
-runtime_gostringnocopy(const byte *str)
-{
-	String s;
-	
-	s.str = str;
-	s.len = runtime_findnull(str);
-	return s;
-}
-
-func cstringToGo(str *byte) (s String) {
-	s = runtime_gostringnocopy(str);
-}
-
-enum
-{
-	Runeself	= 0x80,
-};
-
-func stringiter(s String, k int) (retk int) {
-	int32 l;
-
-	if(k >= s.len) {
-		// retk=0 is end of iteration
-		retk = 0;
-		goto out;
-	}
-
-	l = s.str[k];
-	if(l < Runeself) {
-		retk = k+1;
-		goto out;
-	}
-
-	// multi-char rune
-	retk = k + charntorune(&l, s.str+k, s.len-k);
-
-out:
-}
-
-func stringiter2(s String, k int) (retk int, retv int32) {
-	if(k >= s.len) {
-		// retk=0 is end of iteration
-		retk = 0;
-		retv = 0;
-		goto out;
-	}
-
-	retv = s.str[k];
-	if(retv < Runeself) {
-		retk = k+1;
-		goto out;
-	}
-
-	// multi-char rune
-	retk = k + charntorune(&retv, s.str+k, s.len-k);
-
-out:
-}