1
0
mirror of https://github.com/golang/go synced 2024-10-04 22:21:22 -06:00
go/src/cmd/gc/fmt.c

1705 lines
36 KiB
C
Raw Normal View History

// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include <u.h>
#include <libc.h>
#include "go.h"
#include "opnames.h"
//
// Format conversions
// %L int Line numbers
//
// %E int etype values (aka 'Kind')
//
// %O int Node Opcodes
// Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg)
//
// %J Node* Node details
// Flags: "%hJ" supresses things not relevant until walk.
//
// %V Val* Constant values
//
// %S Sym* Symbols
// Flags: +,- #: mode (see below)
// "%hS" unqualified identifier in any mode
// "%hhS" in export mode: unqualified identifier if exported, qualified if not
//
// %T Type* Types
// Flags: +,- #: mode (see below)
// 'l' definition instead of name.
// 'h' omit "func" and receiver in function types
// 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix.
//
// %N Node* Nodes
// Flags: +,- #: mode (see below)
// 'h' (only in +/debug mode) suppress recursion
// 'l' (only in Error mode) print "foo (type Bar)"
//
// %H NodeList* NodeLists
// Flags: those of %N
// ',' separate items with ',' instead of ';'
//
// %Z Strlit* String literals
//
// In mparith1.c:
// %B Mpint* Big integers
// %F Mpflt* Big floats
//
// %S, %T and %N obey use the following flags to set the format mode:
enum {
FErr, // error mode (default)
FDbg, // "%+N" debug mode
FExp, // "%#N" export mode
FTypeId, // "%-N" turning-types-into-symbols-mode: identical types give identical strings
};
static int fmtmode;
static int fmtpkgpfx; // %uT stickyness
//
// E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode.
//
// The mode flags +, - and # are sticky, meaning they persist through
// recursions of %N, %T and %S, but not the h and l flags. The u flag is
// sticky only on %T recursions and only used in %-/Sym mode.
//
// Useful format combinations:
//
// %+N %+H multiline recursive debug dump of node/nodelist
// %+hN %+hH non recursive debug dump
//
// %#N %#T export format
// %#lT type definition instead of name
// %#hT omit"func" and receiver in function signature
//
// %lN "foo (type Bar)" for error messages
//
// %-T type identifiers
// %-hT type identifiers without "func" and arg names in type signatures (methodsym)
// %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash)
//
static int
setfmode(unsigned long *flags)
{
int fm;
fm = fmtmode;
if(*flags & FmtSign)
fmtmode = FDbg;
else if(*flags & FmtSharp)
fmtmode = FExp;
else if(*flags & FmtLeft)
fmtmode = FTypeId;
*flags &= ~(FmtSharp|FmtLeft|FmtSign);
return fm;
}
// Fmt "%L": Linenumbers
static int
Lconv(Fmt *fp)
{
struct
{
Hist* incl; /* start of this include file */
int32 idel; /* delta line number to apply to include */
Hist* line; /* start of this #line directive */
int32 ldel; /* delta line number to apply to #line */
} a[HISTSZ];
int32 lno, d;
int i, n;
Hist *h;
lno = va_arg(fp->args, int32);
n = 0;
for(h=hist; h!=H; h=h->link) {
if(h->offset < 0)
continue;
if(lno < h->line)
break;
if(h->name) {
if(h->offset > 0) {
// #line directive
if(n > 0 && n < HISTSZ) {
a[n-1].line = h;
a[n-1].ldel = h->line - h->offset + 1;
}
} else {
// beginning of file
if(n < HISTSZ) {
a[n].incl = h;
a[n].idel = h->line;
a[n].line = 0;
}
n++;
}
continue;
}
n--;
if(n > 0 && n < HISTSZ) {
d = h->line - a[n].incl->line;
a[n-1].ldel += d;
a[n-1].idel += d;
}
}
if(n > HISTSZ)
n = HISTSZ;
for(i=n-1; i>=0; i--) {
if(i != n-1) {
if(fp->flags & ~(FmtWidth|FmtPrec))
break;
fmtprint(fp, " ");
}
if(debug['L'] || (fp->flags&FmtLong))
fmtprint(fp, "%s/", pathname);
if(a[i].line)
fmtprint(fp, "%s:%d[%s:%d]",
a[i].line->name, lno-a[i].ldel+1,
a[i].incl->name, lno-a[i].idel+1);
else
fmtprint(fp, "%s:%d",
a[i].incl->name, lno-a[i].idel+1);
lno = a[i].incl->line - 1; // now print out start of this file
}
if(n == 0)
fmtprint(fp, "<unknown line number>");
return 0;
}
static char*
goopnames[] =
{
[OADDR] = "&",
[OADD] = "+",
[OADDSTR] = "+",
[OANDAND] = "&&",
[OANDNOT] = "&^",
[OAND] = "&",
[OAPPEND] = "append",
[OAS] = "=",
[OAS2] = "=",
[OBREAK] = "break",
[OCALL] = "function call", // not actual syntax
[OCAP] = "cap",
[OCASE] = "case",
[OCLOSE] = "close",
[OCOMPLEX] = "complex",
[OCOM] = "^",
[OCONTINUE] = "continue",
[OCOPY] = "copy",
[ODEC] = "--",
[ODELETE] = "delete",
[ODEFER] = "defer",
[ODIV] = "/",
[OEQ] = "==",
[OFALL] = "fallthrough",
[OFOR] = "for",
[OGE] = ">=",
[OGOTO] = "goto",
[OGT] = ">",
[OIF] = "if",
[OIMAG] = "imag",
[OINC] = "++",
[OIND] = "*",
[OLEN] = "len",
[OLE] = "<=",
[OLSH] = "<<",
[OLT] = "<",
[OMAKE] = "make",
[OMINUS] = "-",
[OMOD] = "%",
[OMUL] = "*",
[ONEW] = "new",
[ONE] = "!=",
[ONOT] = "!",
[OOROR] = "||",
[OOR] = "|",
[OPANIC] = "panic",
[OPLUS] = "+",
[OPRINTN] = "println",
[OPRINT] = "print",
[ORANGE] = "range",
[OREAL] = "real",
[ORECV] = "<-",
[ORECOVER] = "recover",
[ORETURN] = "return",
[ORSH] = ">>",
[OSELECT] = "select",
[OSEND] = "<-",
[OSUB] = "-",
[OSWITCH] = "switch",
[OXOR] = "^",
};
// Fmt "%O": Node opcodes
static int
Oconv(Fmt *fp)
{
int o;
o = va_arg(fp->args, int);
if((fp->flags & FmtSharp) || fmtmode != FDbg)
if(o >= 0 && o < nelem(goopnames) && goopnames[o] != nil)
return fmtstrcpy(fp, goopnames[o]);
if(o >= 0 && o < nelem(opnames) && opnames[o] != nil)
return fmtstrcpy(fp, opnames[o]);
return fmtprint(fp, "O-%d", o);
}
static const char* classnames[] = {
"Pxxx",
"PEXTERN",
"PAUTO",
"PPARAM",
"PPARAMOUT",
"PPARAMREF",
"PFUNC",
};
// Fmt "%J": Node details.
static int
Jconv(Fmt *fp)
{
Node *n;
char *s;
int c;
n = va_arg(fp->args, Node*);
c = fp->flags&FmtShort;
if(!c && n->ullman != 0)
fmtprint(fp, " u(%d)", n->ullman);
if(!c && n->addable != 0)
fmtprint(fp, " a(%d)", n->addable);
if(!c && n->vargen != 0)
fmtprint(fp, " g(%d)", n->vargen);
if(n->lineno != 0)
fmtprint(fp, " l(%d)", n->lineno);
if(!c && n->xoffset != BADWIDTH)
fmtprint(fp, " x(%lld%+lld)", n->xoffset, n->stkdelta);
if(n->class != 0) {
s = "";
if(n->class & PHEAP) s = ",heap";
if((n->class & ~PHEAP) < nelem(classnames))
fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
else
fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
}
if(n->colas != 0)
fmtprint(fp, " colas(%d)", n->colas);
if(n->funcdepth != 0)
fmtprint(fp, " f(%d)", n->funcdepth);
switch(n->esc) {
case EscUnknown:
break;
case EscHeap:
fmtprint(fp, " esc(h)");
break;
case EscScope:
fmtprint(fp, " esc(s)");
break;
case EscNone:
fmtprint(fp, " esc(no)");
break;
case EscNever:
if(!c)
fmtprint(fp, " esc(N)");
break;
default:
fmtprint(fp, " esc(%d)", n->esc);
break;
}
if(n->escloopdepth)
fmtprint(fp, " ld(%d)", n->escloopdepth);
if(!c && n->typecheck != 0)
fmtprint(fp, " tc(%d)", n->typecheck);
if(!c && n->dodata != 0)
fmtprint(fp, " dd(%d)", n->dodata);
if(n->isddd != 0)
fmtprint(fp, " isddd(%d)", n->isddd);
if(n->implicit != 0)
fmtprint(fp, " implicit(%d)", n->implicit);
if(n->embedded != 0)
fmtprint(fp, " embedded(%d)", n->embedded);
if(!c && n->used != 0)
fmtprint(fp, " used(%d)", n->used);
return 0;
}
// Fmt "%V": Values
static int
Vconv(Fmt *fp)
{
Val *v;
vlong x;
v = va_arg(fp->args, Val*);
switch(v->ctype) {
case CTINT:
if((fp->flags & FmtSharp) || fmtmode == FExp)
return fmtprint(fp, "%#B", v->u.xval);
return fmtprint(fp, "%B", v->u.xval);
case CTRUNE:
x = mpgetfix(v->u.xval);
if(' ' <= x && x < 0x80 && x != '\\' && x != '\'')
return fmtprint(fp, "'%c'", (int)x);
if(0 <= x && x < (1<<16))
return fmtprint(fp, "'\\u%04ux'", (int)x);
if(0 <= x && x <= Runemax)
return fmtprint(fp, "'\\U%08llux'", x);
return fmtprint(fp, "('\\x00' + %B)", v->u.xval);
case CTFLT:
if((fp->flags & FmtSharp) || fmtmode == FExp)
return fmtprint(fp, "%F", v->u.fval);
return fmtprint(fp, "%#F", v->u.fval);
case CTCPLX:
if((fp->flags & FmtSharp) || fmtmode == FExp)
return fmtprint(fp, "(%F+%Fi)", &v->u.cval->real, &v->u.cval->imag);
if(mpcmpfltc(&v->u.cval->real, 0) == 0)
return fmtprint(fp, "%#Fi", &v->u.cval->imag);
if(mpcmpfltc(&v->u.cval->imag, 0) == 0)
return fmtprint(fp, "%#F", &v->u.cval->real);
if(mpcmpfltc(&v->u.cval->imag, 0) < 0)
return fmtprint(fp, "(%#F%#Fi)", &v->u.cval->real, &v->u.cval->imag);
return fmtprint(fp, "(%#F+%#Fi)", &v->u.cval->real, &v->u.cval->imag);
case CTSTR:
return fmtprint(fp, "\"%Z\"", v->u.sval);
case CTBOOL:
if( v->u.bval)
return fmtstrcpy(fp, "true");
return fmtstrcpy(fp, "false");
case CTNIL:
return fmtstrcpy(fp, "nil");
}
return fmtprint(fp, "<ctype=%d>", v->ctype);
}
// Fmt "%Z": escaped string literals
static int
Zconv(Fmt *fp)
{
Rune r;
Strlit *sp;
char *s, *se;
int n;
sp = va_arg(fp->args, Strlit*);
if(sp == nil)
return fmtstrcpy(fp, "<nil>");
s = sp->s;
se = s + sp->len;
while(s < se) {
n = chartorune(&r, s);
s += n;
switch(r) {
case Runeerror:
if(n == 1) {
fmtprint(fp, "\\x%02x", (uchar)*(s-1));
break;
}
// fall through
default:
if(r < ' ') {
fmtprint(fp, "\\x%02x", r);
break;
}
fmtrune(fp, r);
break;
case '\t':
fmtstrcpy(fp, "\\t");
break;
case '\n':
fmtstrcpy(fp, "\\n");
break;
case '\"':
case '\\':
fmtrune(fp, '\\');
fmtrune(fp, r);
break;
}
}
return 0;
}
/*
s%,%,\n%g
s%\n+%\n%g
s%^[ ]*T%%g
s%,.*%%g
s%.+% [T&] = "&",%g
s%^ ........*\]%&~%g
s%~ %%g
*/
static char*
etnames[] =
{
[TINT] = "INT",
[TUINT] = "UINT",
[TINT8] = "INT8",
[TUINT8] = "UINT8",
[TINT16] = "INT16",
[TUINT16] = "UINT16",
[TINT32] = "INT32",
[TUINT32] = "UINT32",
[TINT64] = "INT64",
[TUINT64] = "UINT64",
[TUINTPTR] = "UINTPTR",
[TFLOAT32] = "FLOAT32",
[TFLOAT64] = "FLOAT64",
[TCOMPLEX64] = "COMPLEX64",
[TCOMPLEX128] = "COMPLEX128",
[TBOOL] = "BOOL",
[TPTR32] = "PTR32",
[TPTR64] = "PTR64",
[TFUNC] = "FUNC",
[TARRAY] = "ARRAY",
[TSTRUCT] = "STRUCT",
[TCHAN] = "CHAN",
[TMAP] = "MAP",
[TINTER] = "INTER",
[TFORW] = "FORW",
[TFIELD] = "FIELD",
[TSTRING] = "STRING",
[TANY] = "ANY",
};
// Fmt "%E": etype
static int
Econv(Fmt *fp)
{
int et;
et = va_arg(fp->args, int);
if(et >= 0 && et < nelem(etnames) && etnames[et] != nil)
return fmtstrcpy(fp, etnames[et]);
return fmtprint(fp, "E-%d", et);
}
// Fmt "%S": syms
static int
symfmt(Fmt *fp, Sym *s)
{
char *p;
if(s->pkg && !(fp->flags&FmtShort)) {
switch(fmtmode) {
case FErr: // This is for the user
if(s->pkg == localpkg)
return fmtstrcpy(fp, s->name);
// If the name was used by multiple packages, display the full path,
if(s->pkg->name && pkglookup(s->pkg->name, nil)->npkg > 1)
return fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
case FDbg:
return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
case FTypeId:
if(fp->flags&FmtUnsigned)
return fmtprint(fp, "%s.%s", s->pkg->name, s->name); // dcommontype, typehash
return fmtprint(fp, "%s.%s", s->pkg->prefix, s->name); // (methodsym), typesym, weaksym
case FExp:
if(s->name && s->name[0] == '.')
fatal("exporting synthetic symbol %s", s->name);
if(s->pkg != builtinpkg)
return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name);
}
}
if(fp->flags&FmtByte) { // FmtByte (hh) implies FmtShort (h)
// skip leading "type." in method name
p = utfrrune(s->name, '.');
if(p)
p++;
else
p = s->name;
// exportname needs to see the name without the prefix too.
if((fmtmode == FExp && !exportname(p)) || fmtmode == FDbg)
return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, p);
return fmtstrcpy(fp, p);
}
return fmtstrcpy(fp, s->name);
}
static char*
basicnames[] =
{
[TINT] = "int",
[TUINT] = "uint",
[TINT8] = "int8",
[TUINT8] = "uint8",
[TINT16] = "int16",
[TUINT16] = "uint16",
[TINT32] = "int32",
[TUINT32] = "uint32",
[TINT64] = "int64",
[TUINT64] = "uint64",
[TUINTPTR] = "uintptr",
[TFLOAT32] = "float32",
[TFLOAT64] = "float64",
[TCOMPLEX64] = "complex64",
[TCOMPLEX128] = "complex128",
[TBOOL] = "bool",
[TANY] = "any",
[TSTRING] = "string",
[TNIL] = "nil",
[TIDEAL] = "ideal",
[TBLANK] = "blank",
};
static int
typefmt(Fmt *fp, Type *t)
{
Type *t1;
Sym *s;
if(t == T)
return fmtstrcpy(fp, "<T>");
if (t == bytetype || t == runetype) {
// in %-T mode collapse rune and byte with their originals.
if(fmtmode != FTypeId)
return fmtprint(fp, "%hS", t->sym);
t = types[t->etype];
}
if(t == errortype)
return fmtstrcpy(fp, "error");
// Unless the 'l' flag was specified, if the type has a name, just print that name.
if(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) {
switch(fmtmode) {
case FTypeId:
if(fp->flags&FmtShort)
return fmtprint(fp, "%hS", t->sym);
if(fp->flags&FmtUnsigned)
return fmtprint(fp, "%uS", t->sym);
// fallthrough
case FExp:
if(t->sym->pkg == localpkg && t->vargen)
return fmtprint(fp, "%S·%d", t->sym, t->vargen);
break;
}
return fmtprint(fp, "%S", t->sym);
}
if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
if(fmtmode == FErr && (t == idealbool || t == idealstring))
fmtstrcpy(fp, "ideal ");
return fmtstrcpy(fp, basicnames[t->etype]);
}
if(fmtmode == FDbg)
fmtprint(fp, "%E-", t->etype);
switch(t->etype) {
case TPTR32:
case TPTR64:
if(fmtmode == FTypeId && (fp->flags&FmtShort))
return fmtprint(fp, "*%hT", t->type);
return fmtprint(fp, "*%T", t->type);
case TARRAY:
if(t->bound >= 0)
return fmtprint(fp, "[%d]%T", (int)t->bound, t->type);
if(t->bound == -100)
return fmtprint(fp, "[...]%T", t->type);
return fmtprint(fp, "[]%T", t->type);
case TCHAN:
switch(t->chan) {
case Crecv:
return fmtprint(fp, "<-chan %T", t->type);
case Csend:
return fmtprint(fp, "chan<- %T", t->type);
}
if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv)
return fmtprint(fp, "chan (%T)", t->type);
return fmtprint(fp, "chan %T", t->type);
case TMAP:
return fmtprint(fp, "map[%T]%T", t->down, t->type);
case TINTER:
fmtstrcpy(fp, "interface {");
for(t1=t->type; t1!=T; t1=t1->down)
if(exportname(t1->sym->name)) {
if(t1->down)
fmtprint(fp, " %hS%hT;", t1->sym, t1->type);
else
fmtprint(fp, " %hS%hT ", t1->sym, t1->type);
} else {
// non-exported method names must be qualified
if(t1->down)
fmtprint(fp, " %uS%hT;", t1->sym, t1->type);
else
fmtprint(fp, " %uS%hT ", t1->sym, t1->type);
}
fmtstrcpy(fp, "}");
return 0;
case TFUNC:
if(fp->flags & FmtShort) {
fmtprint(fp, "%T", getinargx(t));
} else {
if(t->thistuple)
fmtprint(fp, "method%T func%T", getthisx(t), getinargx(t));
else
fmtprint(fp, "func%T", getinargx(t));
}
switch(t->outtuple) {
case 0:
break;
case 1:
if(fmtmode != FExp) {
fmtprint(fp, " %T", getoutargx(t)->type->type); // struct->field->field's type
break;
}
default:
fmtprint(fp, " %T", getoutargx(t));
break;
}
return 0;
case TSTRUCT:
if(t->funarg) {
fmtstrcpy(fp, "(");
if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape" tags
for(t1=t->type; t1!=T; t1=t1->down)
if(t1->down)
fmtprint(fp, "%hT, ", t1);
else
fmtprint(fp, "%hT", t1);
} else {
for(t1=t->type; t1!=T; t1=t1->down)
if(t1->down)
fmtprint(fp, "%T, ", t1);
else
fmtprint(fp, "%T", t1);
}
fmtstrcpy(fp, ")");
} else {
fmtstrcpy(fp, "struct {");
for(t1=t->type; t1!=T; t1=t1->down)
if(t1->down)
fmtprint(fp, " %lT;", t1);
else
fmtprint(fp, " %lT ", t1);
fmtstrcpy(fp, "}");
}
return 0;
case TFIELD:
if(!(fp->flags&FmtShort)) {
s = t->sym;
cmd/gc: emit explicit type information for local variables The type information is (and for years has been) included as an extra field in the address chunk of an instruction. Unfortunately, suppose there is a string at a+24(FP) and we have an instruction reading its length. It will say: MOVQ x+32(FP), AX and the type of *that* argument is int (not slice), because it is the length being read. This confuses the picture seen by debuggers and now, worse, by the garbage collector. Instead of attaching the type information to all uses, emit an explicit list of TYPE instructions with the information. The TYPE instructions are no-ops whose only role is to provide an address to attach type information to. For example, this function: func f(x, y, z int) (a, b string) { return } now compiles into: --- prog list "f" --- 0000 (/Users/rsc/x.go:3) TEXT f+0(SB),$0-56 0001 (/Users/rsc/x.go:3) LOCALS , 0002 (/Users/rsc/x.go:3) TYPE x+0(FP){int},$8 0003 (/Users/rsc/x.go:3) TYPE y+8(FP){int},$8 0004 (/Users/rsc/x.go:3) TYPE z+16(FP){int},$8 0005 (/Users/rsc/x.go:3) TYPE a+24(FP){string},$16 0006 (/Users/rsc/x.go:3) TYPE b+40(FP){string},$16 0007 (/Users/rsc/x.go:3) MOVQ $0,b+40(FP) 0008 (/Users/rsc/x.go:3) MOVQ $0,b+48(FP) 0009 (/Users/rsc/x.go:3) MOVQ $0,a+24(FP) 0010 (/Users/rsc/x.go:3) MOVQ $0,a+32(FP) 0011 (/Users/rsc/x.go:4) RET , The { } show the formerly hidden type information. The { } syntax is used when printing from within the gc compiler. It is not accepted by the assemblers. The same type information is now included on global variables: 0055 (/Users/rsc/x.go:15) GLOBL slice+0(SB){[]string},$24(AL*0) This more accurate type information fixes a bug in the garbage collector's precise heap collection. The linker only cares about globals right now, but having the local information should make things a little nicer for Carl in the future. Fixes #4907. R=ken2 CC=golang-dev https://golang.org/cl/7395056
2013-02-25 10:13:47 -07:00
// Take the name from the original, lest we substituted it with ~anon%d
if ((fmtmode == FErr || fmtmode == FExp) && t->nname != N) {
if(t->nname->orig != N) {
s = t->nname->orig->sym;
cmd/gc: emit explicit type information for local variables The type information is (and for years has been) included as an extra field in the address chunk of an instruction. Unfortunately, suppose there is a string at a+24(FP) and we have an instruction reading its length. It will say: MOVQ x+32(FP), AX and the type of *that* argument is int (not slice), because it is the length being read. This confuses the picture seen by debuggers and now, worse, by the garbage collector. Instead of attaching the type information to all uses, emit an explicit list of TYPE instructions with the information. The TYPE instructions are no-ops whose only role is to provide an address to attach type information to. For example, this function: func f(x, y, z int) (a, b string) { return } now compiles into: --- prog list "f" --- 0000 (/Users/rsc/x.go:3) TEXT f+0(SB),$0-56 0001 (/Users/rsc/x.go:3) LOCALS , 0002 (/Users/rsc/x.go:3) TYPE x+0(FP){int},$8 0003 (/Users/rsc/x.go:3) TYPE y+8(FP){int},$8 0004 (/Users/rsc/x.go:3) TYPE z+16(FP){int},$8 0005 (/Users/rsc/x.go:3) TYPE a+24(FP){string},$16 0006 (/Users/rsc/x.go:3) TYPE b+40(FP){string},$16 0007 (/Users/rsc/x.go:3) MOVQ $0,b+40(FP) 0008 (/Users/rsc/x.go:3) MOVQ $0,b+48(FP) 0009 (/Users/rsc/x.go:3) MOVQ $0,a+24(FP) 0010 (/Users/rsc/x.go:3) MOVQ $0,a+32(FP) 0011 (/Users/rsc/x.go:4) RET , The { } show the formerly hidden type information. The { } syntax is used when printing from within the gc compiler. It is not accepted by the assemblers. The same type information is now included on global variables: 0055 (/Users/rsc/x.go:15) GLOBL slice+0(SB){[]string},$24(AL*0) This more accurate type information fixes a bug in the garbage collector's precise heap collection. The linker only cares about globals right now, but having the local information should make things a little nicer for Carl in the future. Fixes #4907. R=ken2 CC=golang-dev https://golang.org/cl/7395056
2013-02-25 10:13:47 -07:00
if(s != S && s->name[0] == '~')
s = S;
} else
s = S;
cmd/gc: emit explicit type information for local variables The type information is (and for years has been) included as an extra field in the address chunk of an instruction. Unfortunately, suppose there is a string at a+24(FP) and we have an instruction reading its length. It will say: MOVQ x+32(FP), AX and the type of *that* argument is int (not slice), because it is the length being read. This confuses the picture seen by debuggers and now, worse, by the garbage collector. Instead of attaching the type information to all uses, emit an explicit list of TYPE instructions with the information. The TYPE instructions are no-ops whose only role is to provide an address to attach type information to. For example, this function: func f(x, y, z int) (a, b string) { return } now compiles into: --- prog list "f" --- 0000 (/Users/rsc/x.go:3) TEXT f+0(SB),$0-56 0001 (/Users/rsc/x.go:3) LOCALS , 0002 (/Users/rsc/x.go:3) TYPE x+0(FP){int},$8 0003 (/Users/rsc/x.go:3) TYPE y+8(FP){int},$8 0004 (/Users/rsc/x.go:3) TYPE z+16(FP){int},$8 0005 (/Users/rsc/x.go:3) TYPE a+24(FP){string},$16 0006 (/Users/rsc/x.go:3) TYPE b+40(FP){string},$16 0007 (/Users/rsc/x.go:3) MOVQ $0,b+40(FP) 0008 (/Users/rsc/x.go:3) MOVQ $0,b+48(FP) 0009 (/Users/rsc/x.go:3) MOVQ $0,a+24(FP) 0010 (/Users/rsc/x.go:3) MOVQ $0,a+32(FP) 0011 (/Users/rsc/x.go:4) RET , The { } show the formerly hidden type information. The { } syntax is used when printing from within the gc compiler. It is not accepted by the assemblers. The same type information is now included on global variables: 0055 (/Users/rsc/x.go:15) GLOBL slice+0(SB){[]string},$24(AL*0) This more accurate type information fixes a bug in the garbage collector's precise heap collection. The linker only cares about globals right now, but having the local information should make things a little nicer for Carl in the future. Fixes #4907. R=ken2 CC=golang-dev https://golang.org/cl/7395056
2013-02-25 10:13:47 -07:00
}
if(s != S && !t->embedded) {
if(t->funarg)
fmtprint(fp, "%N ", t->nname);
else if(fp->flags&FmtLong)
fmtprint(fp, "%hhS ", s); // qualify non-exported names (used on structs, not on funarg)
else
fmtprint(fp, "%S ", s);
} else if(fmtmode == FExp) {
// TODO(rsc) this breaks on the eliding of unused arguments in the backend
// when this is fixed, the special case in dcl.c checkarglist can go.
//if(t->funarg)
// fmtstrcpy(fp, "_ ");
//else
fmtstrcpy(fp, "? ");
}
}
if(t->isddd)
fmtprint(fp, "...%T", t->type->type);
else
fmtprint(fp, "%T", t->type);
if(!(fp->flags&FmtShort) && t->note)
fmtprint(fp, " \"%Z\"", t->note);
return 0;
case TFORW:
if(t->sym)
return fmtprint(fp, "undefined %S", t->sym);
return fmtstrcpy(fp, "undefined");
case TUNSAFEPTR:
if(fmtmode == FExp)
return fmtprint(fp, "@\"unsafe\".Pointer");
return fmtprint(fp, "unsafe.Pointer");
}
if(fmtmode == FExp)
fatal("missing %E case during export", t->etype);
// Don't know how to handle - fall back to detailed prints.
return fmtprint(fp, "%E <%S> %T", t->etype, t->sym, t->type);
}
// Statements which may be rendered with a simplestmt as init.
static int
stmtwithinit(int op)
{
switch(op) {
case OIF:
case OFOR:
case OSWITCH:
return 1;
}
return 0;
}
static int
stmtfmt(Fmt *f, Node *n)
{
int complexinit, simpleinit, extrablock;
// some statements allow for an init, but at most one,
// but we may have an arbitrary number added, eg by typecheck
// and inlining. If it doesn't fit the syntax, emit an enclosing
// block starting with the init statements.
// if we can just say "for" n->ninit; ... then do so
simpleinit = n->ninit && !n->ninit->next && !n->ninit->n->ninit && stmtwithinit(n->op);
// otherwise, print the inits as separate statements
complexinit = n->ninit && !simpleinit && (fmtmode != FErr);
// but if it was for if/for/switch, put in an extra surrounding block to limit the scope
extrablock = complexinit && stmtwithinit(n->op);
if(extrablock)
fmtstrcpy(f, "{");
if(complexinit)
fmtprint(f, " %H; ", n->ninit);
switch(n->op){
case ODCL:
if(fmtmode == FExp) {
switch(n->left->class&~PHEAP) {
case PPARAM:
case PPARAMOUT:
case PAUTO:
fmtprint(f, "var %N %T", n->left, n->left->type);
goto ret;
}
}
fmtprint(f, "var %S %T", n->left->sym, n->left->type);
break;
case ODCLFIELD:
if(n->left)
fmtprint(f, "%N %N", n->left, n->right);
else
fmtprint(f, "%N", n->right);
break;
case OAS:
// Don't export "v = <N>" initializing statements, hope they're always
// preceded by the DCL which will be re-parsed and typecheck to reproduce
// the "v = <N>" again.
if(fmtmode == FExp && n->right == N)
break;
if(n->colas && !complexinit)
fmtprint(f, "%N := %N", n->left, n->right);
else
fmtprint(f, "%N = %N", n->left, n->right);
break;
case OASOP:
fmtprint(f, "%N %#O= %N", n->left, n->etype, n->right);
break;
case OAS2:
if(n->colas && !complexinit) {
fmtprint(f, "%,H := %,H", n->list, n->rlist);
break;
}
// fallthrough
case OAS2DOTTYPE:
case OAS2FUNC:
case OAS2MAPR:
case OAS2RECV:
fmtprint(f, "%,H = %,H", n->list, n->rlist);
break;
case ORETURN:
fmtprint(f, "return %,H", n->list);
break;
case OPROC:
fmtprint(f, "go %N", n->left);
break;
case ODEFER:
fmtprint(f, "defer %N", n->left);
break;
case OIF:
if(simpleinit)
fmtprint(f, "if %N; %N { %H }", n->ninit->n, n->ntest, n->nbody);
else
fmtprint(f, "if %N { %H }", n->ntest, n->nbody);
if(n->nelse)
fmtprint(f, " else { %H }", n->nelse);
break;
case OFOR:
if(fmtmode == FErr) { // TODO maybe only if FmtShort, same below
fmtstrcpy(f, "for loop");
break;
}
fmtstrcpy(f, "for");
if(simpleinit)
fmtprint(f, " %N;", n->ninit->n);
else if(n->nincr)
fmtstrcpy(f, " ;");
if(n->ntest)
fmtprint(f, " %N", n->ntest);
if(n->nincr)
fmtprint(f, "; %N", n->nincr);
else if(simpleinit)
fmtstrcpy(f, ";");
fmtprint(f, " { %H }", n->nbody);
break;
case ORANGE:
if(fmtmode == FErr) {
fmtstrcpy(f, "for loop");
break;
}
fmtprint(f, "for %,H = range %N { %H }", n->list, n->right, n->nbody);
break;
case OSELECT:
case OSWITCH:
if(fmtmode == FErr) {
fmtprint(f, "%O statement", n->op);
break;
}
fmtprint(f, "%#O", n->op);
if(simpleinit)
fmtprint(f, " %N;", n->ninit->n);
if(n->ntest)
fmtprint(f, "%N", n->ntest);
fmtprint(f, " { %H }", n->list);
break;
case OCASE:
case OXCASE:
if(n->list)
fmtprint(f, "case %,H: %H", n->list, n->nbody);
else
fmtprint(f, "default: %H", n->nbody);
break;
case OBREAK:
case OCONTINUE:
case OGOTO:
case OFALL:
case OXFALL:
if(n->left)
fmtprint(f, "%#O %N", n->op, n->left);
else
fmtprint(f, "%#O", n->op);
break;
case OEMPTY:
break;
case OLABEL:
fmtprint(f, "%N: ", n->left);
break;
}
ret:
if(extrablock)
fmtstrcpy(f, "}");
return 0;
}
static int opprec[] = {
[OAPPEND] = 8,
[OARRAYBYTESTR] = 8,
[OARRAYLIT] = 8,
[OARRAYRUNESTR] = 8,
[OCALLFUNC] = 8,
[OCALLINTER] = 8,
[OCALLMETH] = 8,
[OCALL] = 8,
[OCAP] = 8,
[OCLOSE] = 8,
[OCONVIFACE] = 8,
[OCONVNOP] = 8,
[OCONV] = 8,
[OCOPY] = 8,
[ODELETE] = 8,
[OLEN] = 8,
[OLITERAL] = 8,
[OMAKESLICE] = 8,
[OMAKE] = 8,
[OMAPLIT] = 8,
[ONAME] = 8,
[ONEW] = 8,
[ONONAME] = 8,
[OPACK] = 8,
[OPANIC] = 8,
[OPAREN] = 8,
[OPRINTN] = 8,
[OPRINT] = 8,
[ORUNESTR] = 8,
[OSTRARRAYBYTE] = 8,
[OSTRARRAYRUNE] = 8,
[OSTRUCTLIT] = 8,
[OTARRAY] = 8,
[OTCHAN] = 8,
[OTFUNC] = 8,
[OTINTER] = 8,
[OTMAP] = 8,
[OTPAREN] = 8,
[OTSTRUCT] = 8,
[OINDEXMAP] = 8,
[OINDEX] = 8,
[OSLICE] = 8,
[OSLICESTR] = 8,
[OSLICEARR] = 8,
[ODOTINTER] = 8,
[ODOTMETH] = 8,
[ODOTPTR] = 8,
[ODOTTYPE2] = 8,
[ODOTTYPE] = 8,
[ODOT] = 8,
[OXDOT] = 8,
[OCALLPART] = 8,
[OPLUS] = 7,
[ONOT] = 7,
[OCOM] = 7,
[OMINUS] = 7,
[OADDR] = 7,
[OIND] = 7,
[ORECV] = 7,
[OMUL] = 6,
[ODIV] = 6,
[OMOD] = 6,
[OLSH] = 6,
[ORSH] = 6,
[OAND] = 6,
[OANDNOT] = 6,
[OADD] = 5,
[OSUB] = 5,
[OOR] = 5,
[OXOR] = 5,
[OEQ] = 4,
[OLT] = 4,
[OLE] = 4,
[OGE] = 4,
[OGT] = 4,
[ONE] = 4,
[OCMPSTR] = 4,
[OCMPIFACE] = 4,
[OSEND] = 3,
[OANDAND] = 2,
[OOROR] = 1,
// Statements handled by stmtfmt
[OAS] = -1,
[OAS2] = -1,
[OAS2DOTTYPE] = -1,
[OAS2FUNC] = -1,
[OAS2MAPR] = -1,
[OAS2RECV] = -1,
[OASOP] = -1,
[OBREAK] = -1,
[OCASE] = -1,
[OCONTINUE] = -1,
[ODCL] = -1,
[ODCLFIELD] = -1,
[ODEFER] = -1,
[OEMPTY] = -1,
[OFALL] = -1,
[OFOR] = -1,
[OIF] = -1,
[OLABEL] = -1,
[OPROC] = -1,
[ORANGE] = -1,
[ORETURN] = -1,
[OSELECT] = -1,
[OSWITCH] = -1,
[OXCASE] = -1,
[OXFALL] = -1,
[OEND] = 0
};
static int
exprfmt(Fmt *f, Node *n, int prec)
{
int nprec;
int ptrlit;
NodeList *l;
while(n && n->implicit && (n->op == OIND || n->op == OADDR))
n = n->left;
if(n == N)
return fmtstrcpy(f, "<N>");
nprec = opprec[n->op];
if(n->op == OTYPE && n->sym != S)
nprec = 8;
if(prec > nprec)
return fmtprint(f, "(%N)", n);
switch(n->op) {
case OPAREN:
return fmtprint(f, "(%N)", n->left);
case ODDDARG:
return fmtprint(f, "... argument");
case OREGISTER:
return fmtprint(f, "%R", n->val.u.reg);
case OLITERAL: // this is a bit of a mess
if(fmtmode == FErr && n->sym != S)
return fmtprint(f, "%S", n->sym);
if(n->val.ctype == CTNIL && n->orig != N && n->orig != n)
return exprfmt(f, n->orig, prec);
if(n->type != T && n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) {
// Need parens when type begins with what might
// be misinterpreted as a unary operator: * or <-.
if(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv))
return fmtprint(f, "(%T)(%V)", n->type, &n->val);
else
return fmtprint(f, "%T(%V)", n->type, &n->val);
}
return fmtprint(f, "%V", &n->val);
case ONAME:
// Special case: name used as local variable in export.
switch(n->class&~PHEAP){
case PAUTO:
case PPARAM:
case PPARAMOUT:
if(fmtmode == FExp && n->sym && !isblanksym(n->sym) && n->vargen > 0)
return fmtprint(f, "%S·%d", n->sym, n->vargen);
}
// Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method,
// but for export, this should be rendered as (*pkg.T).meth.
// These nodes have the special property that they are names with a left OTYPE and a right ONAME.
if(fmtmode == FExp && n->left && n->left->op == OTYPE && n->right && n->right->op == ONAME) {
if(isptr[n->left->type->etype])
return fmtprint(f, "(%T).%hhS", n->left->type, n->right->sym);
else
return fmtprint(f, "%T.%hhS", n->left->type, n->right->sym);
}
//fallthrough
case OPACK:
case ONONAME:
return fmtprint(f, "%S", n->sym);
case OTYPE:
if(n->type == T && n->sym != S)
return fmtprint(f, "%S", n->sym);
return fmtprint(f, "%T", n->type);
case OTARRAY:
if(n->left)
return fmtprint(f, "[]%N", n->left);
return fmtprint(f, "[]%N", n->right); // happens before typecheck
case OTPAREN:
return fmtprint(f, "(%N)", n->left);
case OTMAP:
return fmtprint(f, "map[%N]%N", n->left, n->right);
case OTCHAN:
switch(n->etype) {
case Crecv:
return fmtprint(f, "<-chan %N", n->left);
case Csend:
return fmtprint(f, "chan<- %N", n->left);
default:
if(n->left != N && n->left->op == TCHAN && n->left->sym == S && n->left->etype == Crecv)
return fmtprint(f, "chan (%N)", n->left);
else
return fmtprint(f, "chan %N", n->left);
}
case OTSTRUCT:
return fmtprint(f, "<struct>");
case OTINTER:
return fmtprint(f, "<inter>");
case OTFUNC:
return fmtprint(f, "<func>");
case OCLOSURE:
if(fmtmode == FErr)
return fmtstrcpy(f, "func literal");
if(n->nbody)
return fmtprint(f, "%T { %H }", n->type, n->nbody);
return fmtprint(f, "%T { %H }", n->type, n->closure->nbody);
case OCOMPLIT:
ptrlit = n->right != N && n->right->implicit && n->right->type && isptr[n->right->type->etype];
if(fmtmode == FErr) {
if(n->right != N && n->right->type != T && !n->implicit) {
if(ptrlit)
return fmtprint(f, "&%T literal", n->right->type->type);
else
return fmtprint(f, "%T literal", n->right->type);
}
return fmtstrcpy(f, "composite literal");
}
if(fmtmode == FExp && ptrlit)
// typecheck has overwritten OIND by OTYPE with pointer type.
return fmtprint(f, "&%T{ %,H }", n->right->type->type, n->list);
return fmtprint(f, "(%N{ %,H })", n->right, n->list);
case OPTRLIT:
if(fmtmode == FExp && n->left->implicit)
return fmtprint(f, "%N", n->left);
return fmtprint(f, "&%N", n->left);
case OSTRUCTLIT:
if(fmtmode == FExp) { // requires special handling of field names
if(n->implicit)
fmtstrcpy(f, "{");
else
fmtprint(f, "(%T{", n->type);
for(l=n->list; l; l=l->next) {
fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right);
if(l->next)
fmtstrcpy(f, ",");
else
fmtstrcpy(f, " ");
}
if(!n->implicit)
return fmtstrcpy(f, "})");
return fmtstrcpy(f, "}");
}
// fallthrough
case OARRAYLIT:
case OMAPLIT:
if(fmtmode == FErr)
return fmtprint(f, "%T literal", n->type);
if(fmtmode == FExp && n->implicit)
return fmtprint(f, "{ %,H }", n->list);
return fmtprint(f, "(%T{ %,H })", n->type, n->list);
case OKEY:
if(n->left && n->right) {
if(fmtmode == FExp && n->left->type && n->left->type->etype == TFIELD) {
// requires special handling of field names
return fmtprint(f, "%hhS:%N", n->left->sym, n->right);
} else
return fmtprint(f, "%N:%N", n->left, n->right);
}
if(!n->left && n->right)
return fmtprint(f, ":%N", n->right);
if(n->left && !n->right)
return fmtprint(f, "%N:", n->left);
return fmtstrcpy(f, ":");
case OXDOT:
case ODOT:
case ODOTPTR:
case ODOTINTER:
case ODOTMETH:
case OCALLPART:
exprfmt(f, n->left, nprec);
if(n->right == N || n->right->sym == S)
return fmtstrcpy(f, ".<nil>");
return fmtprint(f, ".%hhS", n->right->sym);
case ODOTTYPE:
case ODOTTYPE2:
exprfmt(f, n->left, nprec);
if(n->right != N)
return fmtprint(f, ".(%N)", n->right);
return fmtprint(f, ".(%T)", n->type);
case OINDEX:
case OINDEXMAP:
case OSLICE:
case OSLICESTR:
case OSLICEARR:
exprfmt(f, n->left, nprec);
return fmtprint(f, "[%N]", n->right);
case OCOPY:
case OCOMPLEX:
return fmtprint(f, "%#O(%N, %N)", n->op, n->left, n->right);
case OCONV:
case OCONVIFACE:
case OCONVNOP:
case OARRAYBYTESTR:
case OARRAYRUNESTR:
case OSTRARRAYBYTE:
case OSTRARRAYRUNE:
case ORUNESTR:
if(n->type == T || n->type->sym == S)
return fmtprint(f, "(%T)(%N)", n->type, n->left);
if(n->left)
return fmtprint(f, "%T(%N)", n->type, n->left);
return fmtprint(f, "%T(%,H)", n->type, n->list);
case OREAL:
case OIMAG:
case OAPPEND:
case OCAP:
case OCLOSE:
case ODELETE:
case OLEN:
case OMAKE:
case ONEW:
case OPANIC:
case ORECOVER:
case OPRINT:
case OPRINTN:
if(n->left)
return fmtprint(f, "%#O(%N)", n->op, n->left);
if(n->isddd)
return fmtprint(f, "%#O(%,H...)", n->op, n->list);
return fmtprint(f, "%#O(%,H)", n->op, n->list);
case OCALL:
case OCALLFUNC:
case OCALLINTER:
case OCALLMETH:
exprfmt(f, n->left, nprec);
if(n->isddd)
return fmtprint(f, "(%,H...)", n->list);
return fmtprint(f, "(%,H)", n->list);
case OMAKEMAP:
case OMAKECHAN:
case OMAKESLICE:
if(n->list) // pre-typecheck
return fmtprint(f, "make(%T, %,H)", n->type, n->list);
if(n->right)
return fmtprint(f, "make(%T, %N, %N)", n->type, n->left, n->right);
if(n->left)
return fmtprint(f, "make(%T, %N)", n->type, n->left);
return fmtprint(f, "make(%T)", n->type);
// Unary
case OPLUS:
case OMINUS:
case OADDR:
case OCOM:
case OIND:
case ONOT:
case ORECV:
if(n->left->op == n->op)
fmtprint(f, "%#O ", n->op);
else
fmtprint(f, "%#O", n->op);
return exprfmt(f, n->left, nprec+1);
// Binary
case OADD:
case OADDSTR:
case OAND:
case OANDAND:
case OANDNOT:
case ODIV:
case OEQ:
case OGE:
case OGT:
case OLE:
case OLT:
case OLSH:
case OMOD:
case OMUL:
case ONE:
case OOR:
case OOROR:
case ORSH:
case OSEND:
case OSUB:
case OXOR:
exprfmt(f, n->left, nprec);
fmtprint(f, " %#O ", n->op);
exprfmt(f, n->right, nprec+1);
return 0;
case OCMPSTR:
case OCMPIFACE:
exprfmt(f, n->left, nprec);
fmtprint(f, " %#O ", n->etype);
exprfmt(f, n->right, nprec+1);
return 0;
}
return fmtprint(f, "<node %O>", n->op);
}
static int
nodefmt(Fmt *f, Node *n)
{
Type *t;
t = n->type;
// we almost always want the original, except in export mode for literals
// this saves the importer some work, and avoids us having to redo some
// special casing for package unsafe
if((fmtmode != FExp || n->op != OLITERAL) && n->orig != N)
n = n->orig;
if(f->flags&FmtLong && t != T) {
if(t->etype == TNIL)
return fmtprint(f, "nil");
else
return fmtprint(f, "%N (type %T)", n, t);
}
// TODO inlining produces expressions with ninits. we can't print these yet.
if(opprec[n->op] < 0)
return stmtfmt(f, n);
return exprfmt(f, n, 0);
}
static int dumpdepth;
static void
indent(Fmt *fp)
{
int i;
fmtstrcpy(fp, "\n");
for(i = 0; i < dumpdepth; ++i)
fmtstrcpy(fp, ". ");
}
static int
nodedump(Fmt *fp, Node *n)
{
int recur;
if(n == N)
return 0;
recur = !(fp->flags&FmtShort);
if(recur) {
indent(fp);
if(dumpdepth > 10)
return fmtstrcpy(fp, "...");
if(n->ninit != nil) {
fmtprint(fp, "%O-init%H", n->op, n->ninit);
indent(fp);
}
}
// fmtprint(fp, "[%p]", n);
switch(n->op) {
default:
fmtprint(fp, "%O%J", n->op, n);
break;
case OREGISTER:
case OINDREG:
fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n);
break;
case OLITERAL:
fmtprint(fp, "%O-%V%J", n->op, &n->val, n);
break;
case ONAME:
case ONONAME:
if(n->sym != S)
fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
else
fmtprint(fp, "%O%J", n->op, n);
if(recur && n->type == T && n->ntype) {
indent(fp);
fmtprint(fp, "%O-ntype%N", n->op, n->ntype);
}
break;
case OASOP:
fmtprint(fp, "%O-%O%J", n->op, n->etype, n);
break;
case OTYPE:
fmtprint(fp, "%O %S%J type=%T", n->op, n->sym, n, n->type);
if(recur && n->type == T && n->ntype) {
indent(fp);
fmtprint(fp, "%O-ntype%N", n->op, n->ntype);
}
break;
}
if(n->sym != S && n->op != ONAME)
fmtprint(fp, " %S G%d", n->sym, n->vargen);
if(n->type != T)
fmtprint(fp, " %T", n->type);
if(recur) {
if(n->left)
fmtprint(fp, "%N", n->left);
if(n->right)
fmtprint(fp, "%N", n->right);
if(n->list) {
indent(fp);
fmtprint(fp, "%O-list%H", n->op, n->list);
}
if(n->rlist) {
indent(fp);
fmtprint(fp, "%O-rlist%H", n->op, n->rlist);
}
if(n->ntest) {
indent(fp);
fmtprint(fp, "%O-test%N", n->op, n->ntest);
}
if(n->nbody) {
indent(fp);
fmtprint(fp, "%O-body%H", n->op, n->nbody);
}
if(n->nelse) {
indent(fp);
fmtprint(fp, "%O-else%H", n->op, n->nelse);
}
if(n->nincr) {
indent(fp);
fmtprint(fp, "%O-incr%N", n->op, n->nincr);
}
}
return 0;
}
// Fmt "%S": syms
// Flags: "%hS" suppresses qualifying with package
static int
Sconv(Fmt *fp)
{
Sym *s;
int r, sm;
unsigned long sf;
s = va_arg(fp->args, Sym*);
if(s == S)
return fmtstrcpy(fp, "<S>");
if(s->name && s->name[0] == '_' && s->name[1] == '\0')
return fmtstrcpy(fp, "_");
sf = fp->flags;
sm = setfmode(&fp->flags);
r = symfmt(fp, s);
fp->flags = sf;
fmtmode = sm;
return r;
}
// Fmt "%T": types.
// Flags: 'l' print definition, not name
// 'h' omit 'func' and receiver from function types, short type names
// 'u' package name, not prefix (FTypeId mode, sticky)
static int
Tconv(Fmt *fp)
{
Type *t;
int r, sm;
unsigned long sf;
t = va_arg(fp->args, Type*);
if(t == T)
return fmtstrcpy(fp, "<T>");
if(t->trecur > 4)
return fmtstrcpy(fp, "<...>");
t->trecur++;
sf = fp->flags;
sm = setfmode(&fp->flags);
if(fmtmode == FTypeId && (sf&FmtUnsigned))
fmtpkgpfx++;
if(fmtpkgpfx)
fp->flags |= FmtUnsigned;
r = typefmt(fp, t);
if(fmtmode == FTypeId && (sf&FmtUnsigned))
fmtpkgpfx--;
fp->flags = sf;
fmtmode = sm;
t->trecur--;
return r;
}
// Fmt '%N': Nodes.
// Flags: 'l' suffix with "(type %T)" where possible
// '+h' in debug mode, don't recurse, no multiline output
static int
Nconv(Fmt *fp)
{
Node *n;
int r, sm;
unsigned long sf;
n = va_arg(fp->args, Node*);
if(n == N)
return fmtstrcpy(fp, "<N>");
sf = fp->flags;
sm = setfmode(&fp->flags);
r = -1;
switch(fmtmode) {
case FErr:
case FExp:
r = nodefmt(fp, n);
break;
case FDbg:
dumpdepth++;
r = nodedump(fp, n);
dumpdepth--;
break;
default:
fatal("unhandled %%N mode");
}
fp->flags = sf;
fmtmode = sm;
return r;
}
// Fmt '%H': NodeList.
// Flags: all those of %N plus ',': separate with comma's instead of semicolons.
static int
Hconv(Fmt *fp)
{
NodeList *l;
int r, sm;
unsigned long sf;
char *sep;
l = va_arg(fp->args, NodeList*);
if(l == nil && fmtmode == FDbg)
return fmtstrcpy(fp, "<nil>");
sf = fp->flags;
sm = setfmode(&fp->flags);
r = 0;
sep = "; ";
if(fmtmode == FDbg)
sep = "\n";
else if(fp->flags & FmtComma)
sep = ", ";
for(;l; l=l->next) {
r += fmtprint(fp, "%N", l->n);
if(l->next)
r += fmtstrcpy(fp, sep);
}
fp->flags = sf;
fmtmode = sm;
return r;
}
void
fmtinstallgo(void)
{
fmtmode = FErr;
fmtinstall('E', Econv); // etype opcodes
fmtinstall('J', Jconv); // all the node flags
fmtinstall('H', Hconv); // node lists
fmtinstall('L', Lconv); // line number
fmtinstall('N', Nconv); // node pointer
fmtinstall('O', Oconv); // node opcodes
fmtinstall('S', Sconv); // sym pointer
fmtinstall('T', Tconv); // type pointer
fmtinstall('V', Vconv); // val pointer
fmtinstall('Z', Zconv); // escaped string
// These are in mparith1.c
fmtinstall('B', Bconv); // big numbers
fmtinstall('F', Fconv); // big float numbers
}
void
dumplist(char *s, NodeList *l)
{
print("%s%+H\n", s, l);
}
void
dump(char *s, Node *n)
{
print("%s [%p]%+N\n", s, n, n);
}