// 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. // "Portable" code generation. // Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h. // Must code to the intersection of the three back ends. #include #include #include "md5.h" #include "gg.h" #include "opt.h" #include "../../runtime/funcdata.h" static void allocauto(Prog* p); static void emitptrargsmap(void); 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) { Plist *pl; Node nod1, *n; Prog *ptxt, *p; int32 lno; Type *t; Iter save; vlong oldstksize; NodeList *l; Sym *gcargs; Sym *gclocals; if(newproc == N) { newproc = sysfunc("newproc"); deferproc = sysfunc("deferproc"); deferreturn = sysfunc("deferreturn"); panicindex = sysfunc("panicindex"); panicslice = sysfunc("panicslice"); throwreturn = sysfunc("throwreturn"); } lno = setlineno(fn); curfn = fn; dowidth(curfn->type); if(fn->nbody == nil) { if(pure_go || strncmp(fn->nname->sym->name, "init·", 6) == 0) { yyerror("missing function body", fn); goto ret; } if(debug['A']) goto ret; emitptrargsmap(); goto ret; } saveerrors(); // set up domain for labels clearlabels(); if(curfn->type->outnamed) { // add clearing of the output parameters t = structfirst(&save, getoutarg(curfn->type)); while(t != T) { if(t->nname != N) { n = nod(OAS, t->nname, N); typecheck(&n, Etop); curfn->nbody = concat(list1(n), curfn->nbody); } t = structnext(&save); } } order(curfn); if(nerrors != 0) goto ret; hasdefer = 0; walk(curfn); if(nerrors != 0) goto ret; if(flag_race) racewalk(curfn); if(nerrors != 0) goto ret; continpc = P; breakpc = P; pl = newplist(); pl->name = linksym(curfn->nname->sym); setlineno(curfn); nodconst(&nod1, types[TINT32], 0); ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1); if(fn->dupok) ptxt->TEXTFLAG |= DUPOK; if(fn->wrapper) ptxt->TEXTFLAG |= WRAPPER; if(fn->needctxt) ptxt->TEXTFLAG |= NEEDCTXT; if(fn->nosplit) ptxt->TEXTFLAG |= NOSPLIT; // Clumsy but important. // See test/recover.go for test cases and src/reflect/value.go // for the actual functions being considered. if(myimportpath != nil && strcmp(myimportpath, "reflect") == 0) { if(strcmp(curfn->nname->sym->name, "callReflect") == 0 || strcmp(curfn->nname->sym->name, "callMethod") == 0) ptxt->TEXTFLAG |= WRAPPER; } afunclit(&ptxt->from, curfn->nname); ginit(); gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps); gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps); for(t=curfn->paramfld; t; t=t->down) gtrack(tracksym(t->type)); for(l=fn->dcl; l; l=l->next) { n = l->n; if(n->op != ONAME) // might be OTYPE or OLITERAL continue; switch(n->class) { case PAUTO: case PPARAM: case PPARAMOUT: nodconst(&nod1, types[TUINTPTR], l->n->type->width); p = gins(ATYPE, l->n, &nod1); p->from.gotype = linksym(ngotype(l->n)); break; } } genlist(curfn->enter); genlist(curfn->nbody); gclean(); checklabels(); if(nerrors != 0) goto ret; if(curfn->endlineno) lineno = curfn->endlineno; if(curfn->type->outtuple != 0) ginscall(throwreturn, 0); ginit(); // 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; pc->as = ARET; // overwrite AEND pc->lineno = lineno; fixjmp(ptxt); if(!debug['N'] || debug['R'] || debug['P']) { regopt(ptxt); nilopt(ptxt); } expandchecks(ptxt); oldstksize = stksize; allocauto(ptxt); if(0) print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize); USED(oldstksize); setlineno(curfn); if((int64)stksize+maxarg > (1ULL<<31)) { yyerror("stack frame too large (>2GB)"); goto ret; } // Emit garbage collection symbols. liveness(curfn, ptxt, gcargs, gclocals); gcsymdup(gcargs); gcsymdup(gclocals); defframe(ptxt); if(0) frame(0); // Remove leftover instrumentation from the instruction stream. removevardef(ptxt); ret: lineno = lno; } static void emitptrargsmap(void) { int nptr, nbitmap, j, off; vlong xoffset; Bvec *bv; Sym *sym; sym = lookup(smprint("%s.args_stackmap", curfn->nname->sym->name)); nptr = curfn->type->argwid / widthptr; bv = bvalloc(nptr*2); nbitmap = 1; if(curfn->type->outtuple > 0) nbitmap = 2; off = duint32(sym, 0, nbitmap); off = duint32(sym, off, bv->n); if(curfn->type->thistuple > 0) { xoffset = 0; twobitwalktype1(getthisx(curfn->type), &xoffset, bv); } if(curfn->type->intuple > 0) { xoffset = 0; twobitwalktype1(getinargx(curfn->type), &xoffset, bv); } for(j = 0; j < bv->n; j += 32) off = duint32(sym, off, bv->b[j/32]); if(curfn->type->outtuple > 0) { xoffset = 0; twobitwalktype1(getoutargx(curfn->type), &xoffset, bv); for(j = 0; j < bv->n; j += 32) off = duint32(sym, off, bv->b[j/32]); } ggloblsym(sym, off, RODATA); free(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. // Because autos are laid out in decreasing addresses // on the stack, pointers first, zeroed things first and decreasing size // really means, in memory, things with pointers needing zeroing at // the top of the stack and increasing in size. // Non-autos sort on offset. static int cmpstackvar(Node *a, Node *b) { int ap, bp; if (a->class != b->class) return (a->class == PAUTO) ? +1 : -1; if (a->class != PAUTO) { if (a->xoffset < b->xoffset) return -1; if (a->xoffset > b->xoffset) return +1; return 0; } if ((a->used == 0) != (b->used == 0)) return b->used - a->used; ap = haspointers(a->type); bp = haspointers(b->type); if(ap != bp) return bp - ap; ap = a->needzero; bp = b->needzero; if(ap != bp) return bp - ap; if(a->type->width < b->type->width) return +1; if(a->type->width > b->type->width) return -1; return strcmp(a->sym->name, b->sym->name); } // TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. static void allocauto(Prog* ptxt) { NodeList *ll; Node* n; vlong w; stksize = 0; stkptrsize = 0; if(curfn->dcl == nil) return; // Mark the PAUTO's unused. for(ll=curfn->dcl; ll != nil; ll=ll->next) if (ll->n->class == PAUTO) ll->n->used = 0; markautoused(ptxt); listsort(&curfn->dcl, cmpstackvar); // Unused autos are at the end, chop 'em off. ll = curfn->dcl; n = ll->n; if (n->class == PAUTO && n->op == ONAME && !n->used) { // No locals used at all curfn->dcl = nil; fixautoused(ptxt); return; } for(ll = curfn->dcl; ll->next != nil; ll=ll->next) { n = ll->next->n; if (n->class == PAUTO && n->op == ONAME && !n->used) { ll->next = nil; curfn->dcl->end = ll; break; } } // Reassign stack offsets of the locals that are still there. for(ll = curfn->dcl; ll != nil; ll=ll->next) { n = ll->n; if (n->class != PAUTO || n->op != ONAME) continue; dowidth(n->type); w = n->type->width; if(w >= MAXWIDTH || w < 0) fatal("bad width"); stksize += w; stksize = rnd(stksize, n->type->align); if(haspointers(n->type)) stkptrsize = stksize; if(thechar == '5') stksize = rnd(stksize, widthptr); if(stksize >= (1ULL<<31)) { setlineno(curfn); yyerror("stack frame too large (>2GB)"); } n->stkdelta = -stksize - n->xoffset; } stksize = rnd(stksize, widthreg); stkptrsize = rnd(stkptrsize, widthreg); fixautoused(ptxt); // The debug information needs accurate offsets on the symbols. for(ll = curfn->dcl; ll != nil; ll=ll->next) { if (ll->n->class != PAUTO || ll->n->op != ONAME) continue; ll->n->xoffset += ll->n->stkdelta; ll->n->stkdelta = 0; } } static void movelargefn(Node*); void movelarge(NodeList *l) { for(; l; l=l->next) if(l->n->op == ODCLFUNC) movelargefn(l->n); } static void movelargefn(Node *fn) { NodeList *l; Node *n; for(l=fn->dcl; l != nil; l=l->next) { n = l->n; if(n->class == PAUTO && n->type != T && n->type->width > MaxStackVarSize) addrescapes(n); } } void cgen_checknil(Node *n) { Node reg; if(disable_checknil) return; // 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 || n->op == OLITERAL) { regalloc(®, types[tptr], n); cgen(n, ®); gins(ACHECKNIL, ®, N); regfree(®); return; } gins(ACHECKNIL, n, N); }