From 24699fb05c897dbaec3fe4f1d565c3c9da5078fc Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 3 Dec 2013 10:39:19 -0800 Subject: [PATCH] runtime: get rid of concatstring's vararg C argument. Pass as a slice of strings instead. For 2-5 strings, implement dedicated routines so no slices are needed. static call counts in the go binary: 2 strings: 342 occurrences 3 strings: 98 4 strings: 30 5 strings: 13 6+ strings: 14 Why? C varags, bad for stack scanning and copying. R=golang-dev, iant CC=golang-dev https://golang.org/cl/36380043 --- src/cmd/gc/builtin.c | 6 +++++- src/cmd/gc/runtime.go | 7 +++++-- src/cmd/gc/walk.c | 38 ++++++++++++++++++++++---------------- src/pkg/runtime/string.goc | 35 ++++++++++++++++++++++++++++------- 4 files changed, 60 insertions(+), 26 deletions(-) diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c index 7d4b3e5590d..f88659ee005 100644 --- a/src/cmd/gc/builtin.c +++ b/src/cmd/gc/builtin.c @@ -23,7 +23,11 @@ char *runtimeimport = "func @\"\".printnl ()\n" "func @\"\".printsp ()\n" "func @\"\".goprintf ()\n" - "func @\"\".concatstring ()\n" + "func @\"\".concatstring2 (? string, ? string) (? string)\n" + "func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n" + "func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n" + "func @\"\".concatstring5 (? string, ? string, ? string, ? string, ? string) (? string)\n" + "func @\"\".concatstrings (? []string) (? string)\n" "func @\"\".cmpstring (? string, ? string) (? int)\n" "func @\"\".eqstring (? string, ? string) (? bool)\n" "func @\"\".intstring (? int64) (? string)\n" diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 34c9e901694..662eb8251fc 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -36,8 +36,11 @@ func printnl() func printsp() func goprintf() -// filled in by compiler: int n, string, string, ... -func concatstring() +func concatstring2(string, string) string +func concatstring3(string, string, string) string +func concatstring4(string, string, string, string) string +func concatstring5(string, string, string, string, string) string +func concatstrings([]string) string func cmpstring(string, string) int func eqstring(string, string) bool diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 4648b748873..a3647a5a9ee 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -2558,33 +2558,39 @@ mapfndel(char *name, Type *t) static Node* addstr(Node *n, NodeList **init) { - Node *r, *cat, *typstr; - NodeList *in, *args; - int i, count; + Node *r, *cat, *slice; + NodeList *args; + int count; + Type *t; count = 0; for(r=n; r->op == OADDSTR; r=r->left) count++; // r->right count++; // r + if(count < 2) + yyerror("addstr count %d too small", count); - // 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; intype->list = in; - cat->ntype->rlist = list1(nod(ODCLFIELD, N, typstr)); - + // build list of string arguments 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); + if(count <= 5) { + // small numbers of strings use direct runtime helpers. + snprint(namebuf, sizeof(namebuf), "concatstring%d", count); + } else { + // large numbers of strings are passed to the runtime as a slice. + strcpy(namebuf, "concatstrings"); + t = typ(TARRAY); + t->type = types[TSTRING]; + t->bound = -1; + slice = nod(OCOMPLIT, N, typenod(t)); + slice->list = args; + slice->esc = EscNone; + args = list1(slice); + } + cat = syslook(namebuf, 1); r = nod(OCALL, cat, N); r->list = args; typecheck(&r, Erv); diff --git a/src/pkg/runtime/string.goc b/src/pkg/runtime/string.goc index b79acbe1c2f..57b3546c3f3 100644 --- a/src/pkg/runtime/string.goc +++ b/src/pkg/runtime/string.goc @@ -179,14 +179,35 @@ concatstring(intgo n, String *s) return out; } -// NOTE: Cannot use func syntax, because we need the ..., -// to signal to the garbage collector that this function does -// not have a fixed size argument count. #pragma textflag NOSPLIT -void -runtime·concatstring(intgo n, String s1, ...) -{ - (&s1)[n] = concatstring(n, &s1); +func concatstring2(s1 String, s2 String) (res String) { + USED(&s2); + res = concatstring(2, &s1); +} +#pragma textflag NOSPLIT +func concatstring3(s1 String, s2 String, s3 String) (res String) { + USED(&s2); + USED(&s3); + res = concatstring(3, &s1); +} +#pragma textflag NOSPLIT +func concatstring4(s1 String, s2 String, s3 String, s4 String) (res String) { + USED(&s2); + USED(&s3); + USED(&s4); + res = concatstring(4, &s1); +} +#pragma textflag NOSPLIT +func concatstring5(s1 String, s2 String, s3 String, s4 String, s5 String) (res String) { + USED(&s2); + USED(&s3); + USED(&s4); + USED(&s5); + res = concatstring(5, &s1); +} +#pragma textflag NOSPLIT +func concatstrings(s Slice) (res String) { + res = concatstring(s.len, (String*)s.array); } func eqstring(s1 String, s2 String) (v bool) {