aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-10-28 21:52:53 -0400
committerRuss Cox <rsc@golang.org>2014-10-28 21:52:53 -0400
commit5e568545991cb358a8a004b21e23ac6fa9801124 (patch)
treeac094c574d3f32f9eca8c2c253e5a2ed5b4d230a
parent5f54f06a359f2973521ff3f42899c12d3a6a7fed (diff)
downloadgo-5e568545991cb358a8a004b21e23ac6fa9801124.tar.gz
go-5e568545991cb358a8a004b21e23ac6fa9801124.zip
cmd/gc: avoid use of goprintf
goprintf is a printf-like print for Go. It is used in the code generated by 'defer print(...)' and 'go print(...)'. Normally print(1, 2, 3) turns into printint(1) printint(2) printint(3) but defer and go need a single function call to give the runtime; they give the runtime something like goprintf("%d%d%d", 1, 2, 3). Variadic functions like goprintf cannot be described in the new type information world, so we have to replace it. Replace with a custom function, so that defer print(1, 2, 3) turns into defer func(a1, a2, a3 int) { print(a1, a2, a3) }(1, 2, 3) (and then the print becomes three different printints as usual). Fixes #8614. LGTM=austin R=austin CC=golang-codereviews, r https://golang.org/cl/159700043
-rw-r--r--src/cmd/gc/builtin.c1
-rw-r--r--src/cmd/gc/runtime.go1
-rw-r--r--src/cmd/gc/walk.c226
-rw-r--r--src/runtime/print1.go27
4 files changed, 117 insertions, 138 deletions
diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c
index 5fbb4f0cf3..fbca4ee5f6 100644
--- a/src/cmd/gc/builtin.c
+++ b/src/cmd/gc/builtin.c
@@ -24,7 +24,6 @@ char *runtimeimport =
"func @\"\".printslice (? any)\n"
"func @\"\".printnl ()\n"
"func @\"\".printsp ()\n"
- "func @\"\".goprintf ()\n"
"func @\"\".concatstring2 (? string, ? string) (? string)\n"
"func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n"
"func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n"
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
index 86afe67f17..0fb15c2651 100644
--- a/src/cmd/gc/runtime.go
+++ b/src/cmd/gc/runtime.go
@@ -36,7 +36,6 @@ func printeface(any)
func printslice(any)
func printnl()
func printsp()
-func goprintf()
func concatstring2(string, string) string
func concatstring3(string, string, string) string
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index b761662d14..e5f2217970 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -7,7 +7,7 @@
#include "go.h"
#include "../ld/textflag.h"
-static Node* walkprint(Node*, NodeList**, int);
+static Node* walkprint(Node*, NodeList**);
static Node* writebarrierfn(char*, Type*, Type*);
static Node* applywritebarrier(Node*, NodeList**);
static Node* mapfn(char*, Type*);
@@ -32,6 +32,7 @@ static void walkmul(Node**, NodeList**);
static void walkdiv(Node**, NodeList**);
static int bounded(Node*, int64);
static Mpint mpzero;
+static void walkprintfunc(Node**, NodeList**);
void
walk(Node *fn)
@@ -226,8 +227,7 @@ walkstmt(Node **np)
switch(n->left->op) {
case OPRINT:
case OPRINTN:
- walkexprlist(n->left->list, &n->ninit);
- n->left = walkprint(n->left, &n->ninit, 1);
+ walkprintfunc(&n->left, &n->ninit);
break;
case OCOPY:
n->left = copyany(n->left, &n->ninit, 1);
@@ -260,8 +260,7 @@ walkstmt(Node **np)
switch(n->left->op) {
case OPRINT:
case OPRINTN:
- walkexprlist(n->left->list, &n->ninit);
- n->left = walkprint(n->left, &n->ninit, 1);
+ walkprintfunc(&n->left, &n->ninit);
break;
case OCOPY:
n->left = copyany(n->left, &n->ninit, 1);
@@ -543,7 +542,7 @@ walkexpr(Node **np, NodeList **init)
case OPRINT:
case OPRINTN:
walkexprlist(n->list, init);
- n = walkprint(n, init, 0);
+ n = walkprint(n, init);
goto ret;
case OPANIC:
@@ -1757,7 +1756,7 @@ ret:
// generate code for print
static Node*
-walkprint(Node *nn, NodeList **init, int defer)
+walkprint(Node *nn, NodeList **init)
{
Node *r;
Node *n;
@@ -1766,7 +1765,6 @@ walkprint(Node *nn, NodeList **init, int defer)
Type *t;
int notfirst, et, op;
NodeList *calls, *intypes, *args;
- Fmt fmt;
on = nil;
op = nn->op;
@@ -1776,20 +1774,9 @@ walkprint(Node *nn, NodeList **init, int defer)
intypes = nil;
args = nil;
- memset(&fmt, 0, sizeof fmt);
- if(defer) {
- // defer print turns into defer printf with format string
- fmtstrinit(&fmt);
- intypes = list(intypes, nod(ODCLFIELD, N, typenod(types[TSTRING])));
- args = list1(nod(OXXX, N, N));
- }
-
for(l=all; l; l=l->next) {
if(notfirst) {
- if(defer)
- fmtprint(&fmt, " ");
- else
- calls = list(calls, mkcall("printsp", T, init));
+ calls = list(calls, mkcall("printsp", T, init));
}
notfirst = op == OPRINTN;
@@ -1817,122 +1804,63 @@ walkprint(Node *nn, NodeList **init, int defer)
t = n->type;
et = n->type->etype;
if(isinter(n->type)) {
- if(defer) {
- if(isnilinter(n->type))
- fmtprint(&fmt, "%%e");
- else
- fmtprint(&fmt, "%%i");
- } else {
- if(isnilinter(n->type))
- on = syslook("printeface", 1);
- else
- on = syslook("printiface", 1);
- argtype(on, n->type); // any-1
- }
+ if(isnilinter(n->type))
+ on = syslook("printeface", 1);
+ else
+ on = syslook("printiface", 1);
+ argtype(on, n->type); // any-1
} else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) {
- if(defer) {
- fmtprint(&fmt, "%%p");
- } else {
- on = syslook("printpointer", 1);
- argtype(on, n->type); // any-1
- }
+ on = syslook("printpointer", 1);
+ argtype(on, n->type); // any-1
} else if(isslice(n->type)) {
- if(defer) {
- fmtprint(&fmt, "%%a");
- } else {
- on = syslook("printslice", 1);
- argtype(on, n->type); // any-1
- }
+ on = syslook("printslice", 1);
+ argtype(on, n->type); // any-1
} else if(isint[et]) {
- if(defer) {
- if(et == TUINT64)
- fmtprint(&fmt, "%%U");
- else {
- fmtprint(&fmt, "%%D");
- t = types[TINT64];
- }
- } else {
- if(et == TUINT64) {
- if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0)
- on = syslook("printhex", 0);
- else
- on = syslook("printuint", 0);
- } else
- on = syslook("printint", 0);
- }
- } else if(isfloat[et]) {
- if(defer) {
- fmtprint(&fmt, "%%f");
- t = types[TFLOAT64];
+ if(et == TUINT64) {
+ if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0)
+ on = syslook("printhex", 0);
+ else
+ on = syslook("printuint", 0);
} else
- on = syslook("printfloat", 0);
+ on = syslook("printint", 0);
+ } else if(isfloat[et]) {
+ on = syslook("printfloat", 0);
} else if(iscomplex[et]) {
- if(defer) {
- fmtprint(&fmt, "%%C");
- t = types[TCOMPLEX128];
- } else
- on = syslook("printcomplex", 0);
+ on = syslook("printcomplex", 0);
} else if(et == TBOOL) {
- if(defer)
- fmtprint(&fmt, "%%t");
- else
- on = syslook("printbool", 0);
+ on = syslook("printbool", 0);
} else if(et == TSTRING) {
- if(defer)
- fmtprint(&fmt, "%%S");
- else
- on = syslook("printstring", 0);
+ on = syslook("printstring", 0);
} else {
badtype(OPRINT, n->type, T);
continue;
}
- if(!defer) {
- t = *getinarg(on->type);
- if(t != nil)
- t = t->type;
- if(t != nil)
- t = t->type;
- }
+ t = *getinarg(on->type);
+ if(t != nil)
+ t = t->type;
+ if(t != nil)
+ t = t->type;
if(!eqtype(t, n->type)) {
n = nod(OCONV, n, N);
n->type = t;
}
- if(defer) {
- intypes = list(intypes, nod(ODCLFIELD, N, typenod(t)));
- args = list(args, n);
- } else {
- r = nod(OCALL, on, N);
- r->list = list1(n);
- calls = list(calls, r);
- }
+ r = nod(OCALL, on, N);
+ r->list = list1(n);
+ calls = list(calls, r);
}
- if(defer) {
- if(op == OPRINTN)
- fmtprint(&fmt, "\n");
- on = syslook("goprintf", 1);
- on->type = functype(nil, intypes, nil);
- args->n = nod(OLITERAL, N, N);
- args->n->val.ctype = CTSTR;
- args->n->val.u.sval = strlit(fmtstrflush(&fmt));
- r = nod(OCALL, on, N);
- r->list = args;
- typecheck(&r, Etop);
- walkexpr(&r, init);
- } else {
- if(op == OPRINTN)
- calls = list(calls, mkcall("printnl", T, nil));
- typechecklist(calls, Etop);
- walkexprlist(calls, init);
+ if(op == OPRINTN)
+ calls = list(calls, mkcall("printnl", T, nil));
+ typechecklist(calls, Etop);
+ walkexprlist(calls, init);
- r = nod(OEMPTY, N, N);
- typecheck(&r, Etop);
- walkexpr(&r, init);
- r->ninit = calls;
- }
+ r = nod(OEMPTY, N, N);
+ typecheck(&r, Etop);
+ walkexpr(&r, init);
+ r->ninit = calls;
return r;
}
@@ -3934,3 +3862,71 @@ candiscard(Node *n)
return 1;
}
+
+// rewrite
+// print(x, y, z)
+// into
+// func(a1, a2, a3) {
+// print(a1, a2, a3)
+// }(x, y, z)
+// and same for println.
+static void
+walkprintfunc(Node **np, NodeList **init)
+{
+ Node *n;
+ Node *a, *fn, *t, *oldfn;
+ NodeList *l, *printargs;
+ int num;
+ char buf[100];
+ static int prgen;
+
+ n = *np;
+
+ if(n->ninit != nil) {
+ walkstmtlist(n->ninit);
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ }
+
+ t = nod(OTFUNC, N, N);
+ num = 0;
+ printargs = nil;
+ for(l=n->list; l != nil; l=l->next) {
+ snprint(buf, sizeof buf, "a%d", num++);
+ a = nod(ODCLFIELD, newname(lookup(buf)), typenod(l->n->type));
+ t->list = list(t->list, a);
+ printargs = list(printargs, a->left);
+ }
+
+ fn = nod(ODCLFUNC, N, N);
+ snprint(buf, sizeof buf, "print·%d", ++prgen);
+ fn->nname = newname(lookup(buf));
+ fn->nname->defn = fn;
+ fn->nname->ntype = t;
+ declare(fn->nname, PFUNC);
+
+ oldfn = curfn;
+ curfn = nil;
+ funchdr(fn);
+
+ a = nod(n->op, N, N);
+ a->list = printargs;
+ typecheck(&a, Etop);
+ walkstmt(&a);
+
+ fn->nbody = list1(a);
+
+ funcbody(fn);
+
+ typecheck(&fn, Etop);
+ typechecklist(fn->nbody, Etop);
+ xtop = list(xtop, fn);
+ curfn = oldfn;
+
+ a = nod(OCALL, N, N);
+ a->left = fn->nname;
+ a->list = n->list;
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *np = a;
+}
diff --git a/src/runtime/print1.go b/src/runtime/print1.go
index 0fa1fb63c4..8f8268873b 100644
--- a/src/runtime/print1.go
+++ b/src/runtime/print1.go
@@ -19,32 +19,17 @@ func bytes(s string) (ret []byte) {
return
}
-// goprintf is the function call that is actually deferred when you write
-// defer print(...)
-// It is otherwise unused. In particular it is not used for ordinary prints.
-// Right now a dynamically allocated string that is being passed as an
-// argument is invisible to the garbage collector and might be collected
-// if that argument list is the only reference. For now we ignore that possibility.
-// To fix, we should change to defer a call to vprintf with a pointer to
-// an argument list on the stack, stored in an appropriately typed
-// struct. golang.org/issue/8614.
-//go:nosplit
-func goprintf(s string) {
- vprintf(s, add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
-}
-
-// printf is only called from C code. It has the same problem as goprintf
-// with strings possibly being collected from underneath.
-// However, the runtime never prints dynamically allocated
-// Go strings using printf. The strings it prints come from the symbol
-// and type tables.
+// printf is only called from C code. It has no type information for the args,
+// but C stacks are ignored by the garbage collector anyway, so having
+// type information would not add anything.
//go:nosplit
func printf(s *byte) {
vprintf(gostringnocopy(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
}
-// sprintf is only called from C code.
-// It has the same problem as goprintf.
+// sprintf is only called from C code. It has no type information for the args,
+// but C stacks are ignored by the garbage collector anyway, so having
+// type information would not add anything.
//go:nosplit
func snprintf(dst *byte, n int32, s *byte) {
buf := (*[1 << 30]byte)(unsafe.Pointer(dst))[0:n:n]