gc: make string x + y + z + ... + w efficient

1 malloc per concatenation.

R=ken2
CC=golang-dev
https://golang.org/cl/2124045
diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot
index ce86e12..12f870d 100644
--- a/src/cmd/gc/builtin.c.boot
+++ b/src/cmd/gc/builtin.c.boot
@@ -20,7 +20,7 @@
 	"func \"\".printnl ()\n"
 	"func \"\".printsp ()\n"
 	"func \"\".printf ()\n"
-	"func \"\".catstring (? string, ? string) string\n"
+	"func \"\".concatstring ()\n"
 	"func \"\".cmpstring (? string, ? string) int\n"
 	"func \"\".slicestring (? string, ? int, ? int) string\n"
 	"func \"\".slicestring1 (? string, ? int) string\n"
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
index 2531344..36ed7e9 100644
--- a/src/cmd/gc/runtime.go
+++ b/src/cmd/gc/runtime.go
@@ -33,7 +33,9 @@
 func printsp()
 func printf()
 
-func catstring(string, string) string
+// filled in by compiler: int n, string, string, ...
+func concatstring()
+
 func cmpstring(string, string) int
 func slicestring(string, int, int) string
 func slicestring1(string, int) string
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index fc8f19e..8039774 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -17,6 +17,7 @@
 static	NodeList*	paramstoheap(Type **argin, int out);
 static	NodeList*	reorder1(NodeList*);
 static	NodeList*	reorder3(NodeList*);
+static	Node*	addstr(Node*, NodeList**);
 
 static	NodeList*	walkdefstack;
 
@@ -1205,10 +1206,7 @@
 		goto ret;
 
 	case OADDSTR:
-		// sys_catstring(s1, s2)
-		n = mkcall("catstring", n->type, init,
-			conv(n->left, types[TSTRING]),
-			conv(n->right, types[TSTRING]));
+		n = addstr(n, init);
 		goto ret;
 
 	case OSLICESTR:
@@ -2234,3 +2232,42 @@
 	argtype(fn, t->type);
 	return fn;
 }
+
+static Node*
+addstr(Node *n, NodeList **init)
+{
+	Node *r, *cat, *typstr;
+	NodeList *in, *args;
+	int i, count;
+	
+	count = 0;
+	for(r=n; r->op == OADDSTR; r=r->left)
+		count++;	// r->right
+	count++;	// r
+
+	// prepare call of runtime.catstring of type int, string, string, string
+	// with as many strings as we have.
+	cat = syslook("concatstring", 1);
+	cat->type = T;
+	cat->ntype = nod(OTFUNC, N, N);
+	in = list1(nod(ODCLFIELD, N, typenod(types[TINT])));	// count
+	typstr = typenod(types[TSTRING]);
+	for(i=0; i<count; i++)
+		in = list(in, nod(ODCLFIELD, N, typstr));
+	cat->ntype->list = in;
+	cat->ntype->rlist = list1(nod(ODCLFIELD, N, typstr));
+
+	args = nil;
+	for(r=n; r->op == OADDSTR; r=r->left)
+		args = concat(list1(conv(r->right, types[TSTRING])), args);
+	args = concat(list1(conv(r, types[TSTRING])), args);
+	args = concat(list1(nodintconst(count)), args);
+
+	r = nod(OCALL, cat, N);
+	r->list = args;
+	typecheck(&r, Erv);
+	walkexpr(&r, init);
+	r->type = n->type;
+	
+	return r;
+}
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index ca76729..88f53e2 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -395,6 +395,7 @@
 void*	mal(uintptr);
 uint32	cmpstring(String, String);
 String	catstring(String, String);
+String	concatstring(int32, String*);
 String	gostring(byte*);
 String  gostringn(byte*, int32);
 String	gostringnocopy(byte*);
diff --git a/src/pkg/runtime/string.goc b/src/pkg/runtime/string.goc
index ec45735..7bf8f8b 100644
--- a/src/pkg/runtime/string.goc
+++ b/src/pkg/runtime/string.goc
@@ -114,9 +114,33 @@
 	return s3;
 }
 
+String
+concatstring(int32 n, String *s)
+{
+	int32 i, l;
+	String out;
 
-func catstring(s1 String, s2 String) (s3 String) {
-	s3 = catstring(s1, s2);
+	l = 0;
+	for(i=0; i<n; i++) {
+		if(l + s[i].len < l)
+			throw("string concatenation too long");
+		l += s[i].len;
+	}
+	
+	out = gostringsize(l);
+	l = 0;
+	for(i=0; i<n; i++) {
+		mcpy(out.str+l, s[i].str, s[i].len);
+		l += s[i].len;
+	}
+	return out;
+}
+
+#pragma textflag 7
+// s1 is the first of n strings.
+// the output string follows.
+func concatstring(n int32, s1 String) {
+	(&s1)[n] = concatstring(n, &s1);
 }
 
 uint32