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:
parent
5f54f06a35
commit
5e56854599
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user