mirror of https://github.com/golang/go.git
540 lines
12 KiB
C
540 lines
12 KiB
C
// 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 <u.h>
|
|
#include <libc.h>
|
|
#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' || thechar == '9')
|
|
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' || thechar == '9') && n->op != OREGISTER) || !n->addable || n->op == OLITERAL) {
|
|
regalloc(®, types[tptr], n);
|
|
cgen(n, ®);
|
|
gins(ACHECKNIL, ®, N);
|
|
regfree(®);
|
|
return;
|
|
}
|
|
gins(ACHECKNIL, n, N);
|
|
}
|