diff options
Diffstat (limited to 'src/cmd/gc/pgen.c')
-rw-r--r-- | src/cmd/gc/pgen.c | 398 |
1 files changed, 169 insertions, 229 deletions
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index 2850af6bb..40620c3da 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -8,30 +8,159 @@ #include <u.h> #include <libc.h> +#include "md5.h" #include "gg.h" #include "opt.h" #include "../../pkg/runtime/funcdata.h" -enum { BitsPerPointer = 2 }; - static void allocauto(Prog* p); -static void dumpgcargs(Node*, Sym*); -static Bvec* dumpgclocals(Node*, Sym*); + +static Sym* +makefuncdatasym(char *namefmt, int64 funcdatakind) +{ + Node nod; + Node *pnod; + Sym *sym; + static int32 nsym; + + snprint(namebuf, sizeof(namebuf), namefmt, nsym++); + sym = lookup(namebuf); + pnod = newname(sym); + pnod->class = PEXTERN; + nodconst(&nod, types[TINT32], funcdatakind); + gins(AFUNCDATA, &nod, pnod); + return sym; +} + +// gvardef inserts a VARDEF for n into the instruction stream. +// VARDEF is an annotation for the liveness analysis, marking a place +// where a complete initialization (definition) of a variable begins. +// Since the liveness analysis can see initialization of single-word +// variables quite easy, gvardef is usually only called for multi-word +// or 'fat' variables, those satisfying isfat(n->type). +// However, gvardef is also called when a non-fat variable is initialized +// via a block move; the only time this happens is when you have +// return f() +// for a function with multiple return values exactly matching the return +// types of the current function. +// +// A 'VARDEF x' annotation in the instruction stream tells the liveness +// analysis to behave as though the variable x is being initialized at that +// point in the instruction stream. The VARDEF must appear before the +// actual (multi-instruction) initialization, and it must also appear after +// any uses of the previous value, if any. For example, if compiling: +// +// x = x[1:] +// +// it is important to generate code like: +// +// base, len, cap = pieces of x[1:] +// VARDEF x +// x = {base, len, cap} +// +// If instead the generated code looked like: +// +// VARDEF x +// base, len, cap = pieces of x[1:] +// x = {base, len, cap} +// +// then the liveness analysis would decide the previous value of x was +// unnecessary even though it is about to be used by the x[1:] computation. +// Similarly, if the generated code looked like: +// +// base, len, cap = pieces of x[1:] +// x = {base, len, cap} +// VARDEF x +// +// then the liveness analysis will not preserve the new value of x, because +// the VARDEF appears to have "overwritten" it. +// +// VARDEF is a bit of a kludge to work around the fact that the instruction +// stream is working on single-word values but the liveness analysis +// wants to work on individual variables, which might be multi-word +// aggregates. It might make sense at some point to look into letting +// the liveness analysis work on single-word values as well, although +// there are complications around interface values, slices, and strings, +// all of which cannot be treated as individual words. +// +// VARKILL is the opposite of VARDEF: it marks a value as no longer needed, +// even if its address has been taken. That is, a VARKILL annotation asserts +// that its argument is certainly dead, for use when the liveness analysis +// would not otherwise be able to deduce that fact. + +static void +gvardefx(Node *n, int as) +{ + if(n == N) + fatal("gvardef nil"); + if(n->op != ONAME) { + yyerror("gvardef %#O; %N", n->op, n); + return; + } + switch(n->class) { + case PAUTO: + case PPARAM: + case PPARAMOUT: + gins(as, N, n); + } +} + +void +gvardef(Node *n) +{ + gvardefx(n, AVARDEF); +} + +void +gvarkill(Node *n) +{ + gvardefx(n, AVARKILL); +} + +static void +removevardef(Prog *firstp) +{ + Prog *p; + + for(p = firstp; p != P; p = p->link) { + while(p->link != P && (p->link->as == AVARDEF || p->link->as == AVARKILL)) + p->link = p->link->link; + if(p->to.type == D_BRANCH) + while(p->to.u.branch != P && (p->to.u.branch->as == AVARDEF || p->to.u.branch->as == AVARKILL)) + p->to.u.branch = p->to.u.branch->link; + } +} + +static void +gcsymdup(Sym *s) +{ + LSym *ls; + uint64 lo, hi; + + ls = linksym(s); + if(ls->nr > 0) + fatal("cannot rosymdup %s with relocations", ls->name); + MD5 d; + md5reset(&d); + md5write(&d, ls->p, ls->np); + lo = md5sum(&d, &hi); + ls->name = smprint("gclocals·%016llux%016llux", lo, hi); + ls->dupok = 1; +} void compile(Node *fn) { - Bvec *bv; Plist *pl; - Node nod1, *n, *gcargsnod, *gclocalsnod; - Prog *ptxt, *p, *p1; + Node nod1, *n; + Prog *ptxt, *p; int32 lno; Type *t; Iter save; vlong oldstksize; NodeList *l; - Sym *gcargssym, *gclocalssym; - static int ngcargs, ngclocals; + Sym *gcargs; + Sym *gclocals; if(newproc == N) { newproc = sysfunc("newproc"); @@ -45,7 +174,7 @@ compile(Node *fn) lno = setlineno(fn); if(fn->nbody == nil) { - if(pure_go || memcmp(fn->nname->sym->name, "init·", 6) == 0) + if(pure_go || strncmp(fn->nname->sym->name, "init·", 6) == 0) yyerror("missing function body", fn); goto ret; } @@ -88,7 +217,7 @@ compile(Node *fn) breakpc = P; pl = newplist(); - pl->name = curfn->nname; + pl->name = linksym(curfn->nname->sym); setlineno(curfn); @@ -98,6 +227,8 @@ compile(Node *fn) ptxt->TEXTFLAG |= DUPOK; if(fn->wrapper) ptxt->TEXTFLAG |= WRAPPER; + if(fn->needctxt) + ptxt->TEXTFLAG |= NEEDCTXT; // Clumsy but important. // See test/recover.go for test cases and src/pkg/reflect/value.go @@ -111,21 +242,8 @@ compile(Node *fn) ginit(); - snprint(namebuf, sizeof namebuf, "gcargs·%d", ngcargs++); - gcargssym = lookup(namebuf); - gcargsnod = newname(gcargssym); - gcargsnod->class = PEXTERN; - - nodconst(&nod1, types[TINT32], FUNCDATA_GCArgs); - gins(AFUNCDATA, &nod1, gcargsnod); - - snprint(namebuf, sizeof(namebuf), "gclocals·%d", ngclocals++); - gclocalssym = lookup(namebuf); - gclocalsnod = newname(gclocalssym); - gclocalsnod->class = PEXTERN; - - nodconst(&nod1, types[TINT32], FUNCDATA_GCLocals); - gins(AFUNCDATA, &nod1, gclocalsnod); + gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps); + gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps); for(t=curfn->paramfld; t; t=t->down) gtrack(tracksym(t->type)); @@ -140,20 +258,12 @@ compile(Node *fn) case PPARAMOUT: nodconst(&nod1, types[TUINTPTR], l->n->type->width); p = gins(ATYPE, l->n, &nod1); - p->from.gotype = ngotype(l->n); + p->from.gotype = linksym(ngotype(l->n)); break; } } genlist(curfn->enter); - - retpc = nil; - if(hasdefer || curfn->exit) { - p1 = gjmp(nil); - retpc = gjmp(nil); - patch(p1, pc); - } - genlist(curfn->nbody); gclean(); checklabels(); @@ -165,13 +275,15 @@ compile(Node *fn) if(curfn->type->outtuple != 0) ginscall(throwreturn, 0); - if(retpc) - patch(retpc, pc); ginit(); - if(hasdefer) - ginscall(deferreturn, 0); - if(curfn->exit) - genlist(curfn->exit); + // TODO: Determine when the final cgen_ret can be omitted. Perhaps always? + cgen_ret(nil); + if(hasdefer) { + // deferreturn pretends to have one uintptr argument. + // Reserve space for it so stack scanner is happy. + if(maxarg < widthptr) + maxarg = widthptr; + } gclean(); if(nerrors != 0) goto ret; @@ -179,6 +291,7 @@ compile(Node *fn) pc->as = ARET; // overwrite AEND pc->lineno = lineno; + fixjmp(ptxt); if(!debug['N'] || debug['R'] || debug['P']) { regopt(ptxt); nilopt(ptxt); @@ -190,6 +303,7 @@ compile(Node *fn) if(0) print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize); + USED(oldstksize); setlineno(curfn); if((int64)stksize+maxarg > (1ULL<<31)) { @@ -198,184 +312,21 @@ compile(Node *fn) } // Emit garbage collection symbols. - dumpgcargs(fn, gcargssym); - bv = dumpgclocals(curfn, gclocalssym); + liveness(curfn, ptxt, gcargs, gclocals); + gcsymdup(gcargs); + gcsymdup(gclocals); - defframe(ptxt, bv); - free(bv); + defframe(ptxt); if(0) frame(0); + // Remove leftover instrumentation from the instruction stream. + removevardef(ptxt); ret: lineno = lno; } -static void -walktype1(Type *t, vlong *xoffset, Bvec *bv) -{ - vlong fieldoffset, i, o; - Type *t1; - - if(t->align > 0 && (*xoffset % t->align) != 0) - fatal("walktype1: invalid initial alignment, %T", t); - - switch(t->etype) { - case TINT8: - case TUINT8: - case TINT16: - case TUINT16: - case TINT32: - case TUINT32: - case TINT64: - case TUINT64: - case TINT: - case TUINT: - case TUINTPTR: - case TBOOL: - case TFLOAT32: - case TFLOAT64: - case TCOMPLEX64: - case TCOMPLEX128: - *xoffset += t->width; - break; - - case TPTR32: - case TPTR64: - case TUNSAFEPTR: - case TFUNC: - case TCHAN: - case TMAP: - if(*xoffset % widthptr != 0) - fatal("walktype1: invalid alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer); - *xoffset += t->width; - break; - - case TSTRING: - // struct { byte *str; intgo len; } - if(*xoffset % widthptr != 0) - fatal("walktype1: invalid alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer); - *xoffset += t->width; - break; - - case TINTER: - // struct { Itab* tab; union { void* ptr, uintptr val } data; } - // or, when isnilinter(t)==true: - // struct { Type* type; union { void* ptr, uintptr val } data; } - if(*xoffset % widthptr != 0) - fatal("walktype1: invalid alignment, %T", t); - bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1); - if(isnilinter(t)) - bvset(bv, ((*xoffset / widthptr) * BitsPerPointer)); - *xoffset += t->width; - break; - - case TARRAY: - // The value of t->bound is -1 for slices types and >0 for - // for fixed array types. All other values are invalid. - if(t->bound < -1) - fatal("walktype1: invalid bound, %T", t); - if(isslice(t)) { - // struct { byte* array; uintgo len; uintgo cap; } - if(*xoffset % widthptr != 0) - fatal("walktype1: invalid TARRAY alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer); - *xoffset += t->width; - } else if(!haspointers(t->type)) - *xoffset += t->width; - else - for(i = 0; i < t->bound; ++i) - walktype1(t->type, xoffset, bv); - break; - - case TSTRUCT: - o = 0; - for(t1 = t->type; t1 != T; t1 = t1->down) { - fieldoffset = t1->width; - *xoffset += fieldoffset - o; - walktype1(t1->type, xoffset, bv); - o = fieldoffset + t1->type->width; - } - *xoffset += t->width - o; - break; - - default: - fatal("walktype1: unexpected type, %T", t); - } -} - -static void -walktype(Type *type, Bvec *bv) -{ - vlong xoffset; - - // Start the walk at offset 0. The correct offset will be - // filled in by the first type encountered during the walk. - xoffset = 0; - walktype1(type, &xoffset, bv); -} - -// Compute a bit vector to describe the pointer-containing locations -// in the in and out argument list and dump the bitvector length and -// data to the provided symbol. -static void -dumpgcargs(Node *fn, Sym *sym) -{ - Type *thistype, *inargtype, *outargtype; - Bvec *bv; - int32 i; - int off; - - thistype = getthisx(fn->type); - inargtype = getinargx(fn->type); - outargtype = getoutargx(fn->type); - bv = bvalloc((fn->type->argwid / widthptr) * BitsPerPointer); - if(thistype != nil) - walktype(thistype, bv); - if(inargtype != nil) - walktype(inargtype, bv); - if(outargtype != nil) - walktype(outargtype, bv); - off = duint32(sym, 0, bv->n); - for(i = 0; i < bv->n; i += 32) - off = duint32(sym, off, bv->b[i/32]); - free(bv); - ggloblsym(sym, off, 0, 1); -} - -// Compute a bit vector to describe the pointer-containing locations -// in local variables and dump the bitvector length and data out to -// the provided symbol. Return the vector for use and freeing by caller. -static Bvec* -dumpgclocals(Node* fn, Sym *sym) -{ - Bvec *bv; - NodeList *ll; - Node *node; - vlong xoffset; - int32 i; - int off; - - bv = bvalloc((stkptrsize / widthptr) * BitsPerPointer); - for(ll = fn->dcl; ll != nil; ll = ll->next) { - node = ll->n; - if(node->class == PAUTO && node->op == ONAME) { - if(haspointers(node->type)) { - xoffset = node->xoffset + stkptrsize; - walktype1(node->type, &xoffset, bv); - } - } - } - off = duint32(sym, 0, bv->n); - for(i = 0; i < bv->n; i += 32) { - off = duint32(sym, off, bv->b[i/32]); - } - ggloblsym(sym, off, 0, 1); - return bv; -} - // Sort the list of stack variables. Autos after anything else, // within autos, unused after used, within used, things with // pointers first, zeroed things first, and then decreasing size. @@ -415,7 +366,8 @@ cmpstackvar(Node *a, Node *b) return +1; if(a->type->width > b->type->width) return -1; - return 0; + + return strcmp(a->sym->name, b->sym->name); } // TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. @@ -428,7 +380,6 @@ allocauto(Prog* ptxt) stksize = 0; stkptrsize = 0; - stkzerosize = 0; if(curfn->dcl == nil) return; @@ -440,13 +391,6 @@ allocauto(Prog* ptxt) markautoused(ptxt); - if(precisestack_enabled) { - // TODO: Remove when liveness analysis sets needzero instead. - for(ll=curfn->dcl; ll != nil; ll=ll->next) - if(ll->n->class == PAUTO) - ll->n->needzero = 1; // ll->n->addrtaken; - } - listsort(&curfn->dcl, cmpstackvar); // Unused autos are at the end, chop 'em off. @@ -480,11 +424,8 @@ allocauto(Prog* ptxt) fatal("bad width"); stksize += w; stksize = rnd(stksize, n->type->align); - if(haspointers(n->type)) { + if(haspointers(n->type)) stkptrsize = stksize; - if(n->needzero) - stkzerosize = stksize; - } if(thechar == '5') stksize = rnd(stksize, widthptr); if(stksize >= (1ULL<<31)) { @@ -493,9 +434,8 @@ allocauto(Prog* ptxt) } n->stkdelta = -stksize - n->xoffset; } - stksize = rnd(stksize, widthptr); - stkptrsize = rnd(stkptrsize, widthptr); - stkzerosize = rnd(stkzerosize, widthptr); + stksize = rnd(stksize, widthreg); + stkptrsize = rnd(stkptrsize, widthreg); fixautoused(ptxt); @@ -538,12 +478,12 @@ cgen_checknil(Node *n) if(disable_checknil) return; - // Ideally we wouldn't see any TUINTPTR here, but we do. - if(n->type == T || (!isptr[n->type->etype] && n->type->etype != TUINTPTR && n->type->etype != TUNSAFEPTR)) { + // Ideally we wouldn't see any integer types here, but we do. + if(n->type == T || (!isptr[n->type->etype] && !isint[n->type->etype] && n->type->etype != TUNSAFEPTR)) { dump("checknil", n); fatal("bad checknil"); } - if((thechar == '5' && n->op != OREGISTER) || !n->addable) { + if((thechar == '5' && n->op != OREGISTER) || !n->addable || n->op == OLITERAL) { regalloc(®, types[tptr], n); cgen(n, ®); gins(ACHECKNIL, ®, N); |