1
0
mirror of https://github.com/golang/go synced 2024-11-19 02:14:43 -07:00

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
This commit is contained in:
Russ Cox 2014-10-28 21:52:53 -04:00
parent 5f54f06a35
commit 5e56854599
4 changed files with 118 additions and 139 deletions

View File

@ -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"

View File

@ -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

View File

@ -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);
}
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];
} else
on = syslook("printfloat", 0);
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);
}
}
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);
r = nod(OEMPTY, N, N);
typecheck(&r, Etop);
walkexpr(&r, init);
r->ninit = calls;
r->list = list1(n);
calls = list(calls, r);
}
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;
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;
}

View File

@ -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]