This source file includes following definitions.
- sysfunc
- addrescapes
- clearlabels
- newlab
- checklabels
- checkgoto
- stmtlabel
- genlist
- gen
- cgen_callmeth
- cgen_proc
- cgen_dcl
- cgen_discard
- clearslim
- cgen_as
- cgen_eface
- cgen_slice
- dotoffset
- tempname
- temp
#include <u.h>
#include <libc.h>
#include "go.h"
static void cgen_dcl(Node *n);
static void cgen_proc(Node *n, int proc);
static void checkgoto(Node*, Node*);
static Label *labellist;
static Label *lastlabel;
Node*
sysfunc(char *name)
{
Node *n;
n = newname(pkglookup(name, runtimepkg));
n->class = PFUNC;
return n;
}
void
addrescapes(Node *n)
{
char buf[100];
Node *oldfn;
switch(n->op) {
default:
break;
case ONAME:
if(n == nodfp)
break;
if(n->class == PAUTO && n->esc == EscNever)
break;
if(debug['N'] && n->esc != EscUnknown)
fatal("without escape analysis, only PAUTO's should have esc: %N", n);
switch(n->class) {
case PPARAMREF:
addrescapes(n->defn);
break;
case PPARAM:
case PPARAMOUT:
n->stackparam = nod(OPARAM, n, N);
n->stackparam->type = n->type;
n->stackparam->addable = 1;
if(n->xoffset == BADWIDTH)
fatal("addrescapes before param assignment");
n->stackparam->xoffset = n->xoffset;
case PAUTO:
n->class |= PHEAP;
n->addable = 0;
n->ullman = 2;
n->xoffset = 0;
oldfn = curfn;
curfn = n->curfn;
n->heapaddr = temp(ptrto(n->type));
snprint(buf, sizeof buf, "&%S", n->sym);
n->heapaddr->sym = lookup(buf);
n->heapaddr->orig->sym = n->heapaddr->sym;
if(!debug['N'])
n->esc = EscHeap;
if(debug['m'])
print("%L: moved to heap: %N\n", n->lineno, n);
curfn = oldfn;
break;
}
break;
case OIND:
case ODOTPTR:
break;
case ODOT:
case OINDEX:
if(!isslice(n->left->type))
addrescapes(n->left);
break;
}
}
void
clearlabels(void)
{
Label *l;
for(l=labellist; l!=L; l=l->link)
l->sym->label = L;
labellist = L;
lastlabel = L;
}
static Label*
newlab(Node *n)
{
Sym *s;
Label *lab;
s = n->left->sym;
if((lab = s->label) == L) {
lab = mal(sizeof(*lab));
if(lastlabel == nil)
labellist = lab;
else
lastlabel->link = lab;
lastlabel = lab;
lab->sym = s;
s->label = lab;
}
if(n->op == OLABEL) {
if(lab->def != N)
yyerror("label %S already defined at %L", s, lab->def->lineno);
else
lab->def = n;
} else
lab->use = list(lab->use, n);
return lab;
}
void
checklabels(void)
{
Label *lab;
NodeList *l;
for(lab=labellist; lab!=L; lab=lab->link) {
if(lab->def == N) {
for(l=lab->use; l; l=l->next)
yyerrorl(l->n->lineno, "label %S not defined", lab->sym);
continue;
}
if(lab->use == nil && !lab->used) {
yyerrorl(lab->def->lineno, "label %S defined and not used", lab->sym);
continue;
}
if(lab->gotopc != P)
fatal("label %S never resolved", lab->sym);
for(l=lab->use; l; l=l->next)
checkgoto(l->n, lab->def);
}
}
static void
checkgoto(Node *from, Node *to)
{
int nf, nt;
Sym *block, *dcl, *fs, *ts;
int lno;
if(from->sym == to->sym)
return;
nf = 0;
for(fs=from->sym; fs; fs=fs->link)
nf++;
nt = 0;
for(fs=to->sym; fs; fs=fs->link)
nt++;
fs = from->sym;
for(; nf > nt; nf--)
fs = fs->link;
if(fs != to->sym) {
lno = lineno;
setlineno(from);
block = S;
dcl = S;
ts = to->sym;
for(; nt > nf; nt--) {
if(ts->pkg == nil)
block = ts;
else
dcl = ts;
ts = ts->link;
}
while(ts != fs) {
if(ts->pkg == nil)
block = ts;
else
dcl = ts;
ts = ts->link;
fs = fs->link;
}
if(block)
yyerror("goto %S jumps into block starting at %L", from->left->sym, block->lastlineno);
else
yyerror("goto %S jumps over declaration of %S at %L", from->left->sym, dcl, dcl->lastlineno);
lineno = lno;
}
}
static Label*
stmtlabel(Node *n)
{
Label *lab;
if(n->sym != S)
if((lab = n->sym->label) != L)
if(lab->def != N)
if(lab->def->defn == n)
return lab;
return L;
}
void
genlist(NodeList *l)
{
for(; l; l=l->next)
gen(l->n);
}
void
gen(Node *n)
{
int32 lno;
Prog *scontin, *sbreak;
Prog *p1, *p2, *p3;
Label *lab;
int32 wasregalloc;
lno = setlineno(n);
wasregalloc = anyregalloc();
if(n == N)
goto ret;
if(n->ninit)
genlist(n->ninit);
setlineno(n);
switch(n->op) {
default:
fatal("gen: unknown op %+hN", n);
break;
case OCASE:
case OFALL:
case OXCASE:
case OXFALL:
case ODCLCONST:
case ODCLFUNC:
case ODCLTYPE:
break;
case OEMPTY:
break;
case OBLOCK:
genlist(n->list);
break;
case OLABEL:
if(isblanksym(n->left->sym))
break;
lab = newlab(n);
for(p1=lab->gotopc; p1; p1=p2) {
p2 = unpatch(p1);
patch(p1, pc);
}
lab->gotopc = P;
if(lab->labelpc == P)
lab->labelpc = pc;
if(n->defn) {
switch(n->defn->op) {
case OFOR:
case OSWITCH:
case OSELECT:
n->defn->sym = lab->sym;
}
}
break;
case OGOTO:
lab = newlab(n);
if(lab->labelpc != P)
gjmp(lab->labelpc);
else
lab->gotopc = gjmp(lab->gotopc);
break;
case OBREAK:
if(n->left != N) {
lab = n->left->sym->label;
if(lab == L) {
yyerror("break label not defined: %S", n->left->sym);
break;
}
lab->used = 1;
if(lab->breakpc == P) {
yyerror("invalid break label %S", n->left->sym);
break;
}
gjmp(lab->breakpc);
break;
}
if(breakpc == P) {
yyerror("break is not in a loop");
break;
}
gjmp(breakpc);
break;
case OCONTINUE:
if(n->left != N) {
lab = n->left->sym->label;
if(lab == L) {
yyerror("continue label not defined: %S", n->left->sym);
break;
}
lab->used = 1;
if(lab->continpc == P) {
yyerror("invalid continue label %S", n->left->sym);
break;
}
gjmp(lab->continpc);
break;
}
if(continpc == P) {
yyerror("continue is not in a loop");
break;
}
gjmp(continpc);
break;
case OFOR:
sbreak = breakpc;
p1 = gjmp(P);
breakpc = gjmp(P);
scontin = continpc;
continpc = pc;
if((lab = stmtlabel(n)) != L) {
lab->breakpc = breakpc;
lab->continpc = continpc;
}
gen(n->nincr);
patch(p1, pc);
bgen(n->ntest, 0, -1, breakpc);
genlist(n->nbody);
gjmp(continpc);
patch(breakpc, pc);
continpc = scontin;
breakpc = sbreak;
if(lab) {
lab->breakpc = P;
lab->continpc = P;
}
break;
case OIF:
p1 = gjmp(P);
p2 = gjmp(P);
patch(p1, pc);
bgen(n->ntest, 0, -n->likely, p2);
genlist(n->nbody);
p3 = gjmp(P);
patch(p2, pc);
genlist(n->nelse);
patch(p3, pc);
break;
case OSWITCH:
sbreak = breakpc;
p1 = gjmp(P);
breakpc = gjmp(P);
if((lab = stmtlabel(n)) != L)
lab->breakpc = breakpc;
patch(p1, pc);
genlist(n->nbody);
patch(breakpc, pc);
breakpc = sbreak;
if(lab != L)
lab->breakpc = P;
break;
case OSELECT:
sbreak = breakpc;
p1 = gjmp(P);
breakpc = gjmp(P);
if((lab = stmtlabel(n)) != L)
lab->breakpc = breakpc;
patch(p1, pc);
genlist(n->nbody);
patch(breakpc, pc);
breakpc = sbreak;
if(lab != L)
lab->breakpc = P;
break;
case OASOP:
cgen_asop(n);
break;
case ODCL:
cgen_dcl(n->left);
break;
case OAS:
if(gen_as_init(n))
break;
cgen_as(n->left, n->right);
break;
case OCALLMETH:
cgen_callmeth(n, 0);
break;
case OCALLINTER:
cgen_callinter(n, N, 0);
break;
case OCALLFUNC:
cgen_call(n, 0);
break;
case OPROC:
cgen_proc(n, 1);
break;
case ODEFER:
cgen_proc(n, 2);
break;
case ORETURN:
case ORETJMP:
cgen_ret(n);
break;
case OCHECKNIL:
cgen_checknil(n->left);
break;
case OVARKILL:
gvarkill(n->left);
break;
}
ret:
if(anyregalloc() != wasregalloc) {
dump("node", n);
fatal("registers left allocated");
}
lineno = lno;
}
void
cgen_callmeth(Node *n, int proc)
{
Node n2;
Node *l;
l = n->left;
if(l->op != ODOTMETH)
fatal("cgen_callmeth: not dotmethod: %N");
n2 = *n;
n2.op = OCALLFUNC;
n2.left = l->right;
n2.left->type = l->type;
if(n2.left->op == ONAME)
n2.left->class = PFUNC;
cgen_call(&n2, proc);
}
static void
cgen_proc(Node *n, int proc)
{
switch(n->left->op) {
default:
fatal("cgen_proc: unknown call %O", n->left->op);
case OCALLMETH:
cgen_callmeth(n->left, proc);
break;
case OCALLINTER:
cgen_callinter(n->left, N, proc);
break;
case OCALLFUNC:
cgen_call(n->left, proc);
break;
}
}
static void
cgen_dcl(Node *n)
{
if(debug['g'])
dump("\ncgen-dcl", n);
if(n->op != ONAME) {
dump("cgen_dcl", n);
fatal("cgen_dcl");
}
if(!(n->class & PHEAP))
return;
if(n->alloc == nil)
n->alloc = callnew(n->type);
cgen_as(n->heapaddr, n->alloc);
}
static void
cgen_discard(Node *nr)
{
Node tmp;
if(nr == N)
return;
switch(nr->op) {
case ONAME:
if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC && nr->class != PPARAMREF)
gused(nr);
break;
case OADD:
case OAND:
case ODIV:
case OEQ:
case OGE:
case OGT:
case OLE:
case OLSH:
case OLT:
case OMOD:
case OMUL:
case ONE:
case OOR:
case ORSH:
case OSUB:
case OXOR:
cgen_discard(nr->left);
cgen_discard(nr->right);
break;
case OCAP:
case OCOM:
case OLEN:
case OMINUS:
case ONOT:
case OPLUS:
cgen_discard(nr->left);
break;
case OIND:
cgen_checknil(nr->left);
break;
default:
tempname(&tmp, nr->type);
cgen_as(&tmp, nr);
gused(&tmp);
}
}
void
clearslim(Node *n)
{
Node z;
Mpflt zero;
memset(&z, 0, sizeof(z));
z.op = OLITERAL;
z.type = n->type;
z.addable = 1;
switch(simtype[n->type->etype]) {
case TCOMPLEX64:
case TCOMPLEX128:
z.val.u.cval = mal(sizeof(*z.val.u.cval));
mpmovecflt(&z.val.u.cval->real, 0.0);
mpmovecflt(&z.val.u.cval->imag, 0.0);
break;
case TFLOAT32:
case TFLOAT64:
mpmovecflt(&zero, 0.0);
z.val.ctype = CTFLT;
z.val.u.fval = &zero;
break;
case TPTR32:
case TPTR64:
case TCHAN:
case TMAP:
z.val.ctype = CTNIL;
break;
case TBOOL:
z.val.ctype = CTBOOL;
break;
case TINT8:
case TINT16:
case TINT32:
case TINT64:
case TUINT8:
case TUINT16:
case TUINT32:
case TUINT64:
z.val.ctype = CTINT;
z.val.u.xval = mal(sizeof(*z.val.u.xval));
mpmovecfix(z.val.u.xval, 0);
break;
default:
fatal("clearslim called on type %T", n->type);
}
ullmancalc(&z);
cgen(&z, n);
}
void
cgen_as(Node *nl, Node *nr)
{
Type *tl;
if(debug['g']) {
dump("cgen_as", nl);
dump("cgen_as = ", nr);
}
while(nr != N && nr->op == OCONVNOP)
nr = nr->left;
if(nl == N || isblank(nl)) {
cgen_discard(nr);
return;
}
if(nr == N || isnil(nr)) {
if(nr == N) {
if(nl->class == PEXTERN)
return;
if(nl->class & PHEAP)
return;
}
tl = nl->type;
if(tl == T)
return;
if(isfat(tl)) {
if(nl->op == ONAME)
gvardef(nl);
clearfat(nl);
return;
}
clearslim(nl);
return;
}
tl = nl->type;
if(tl == T)
return;
cgen(nr, nl);
}
void
cgen_eface(Node *n, Node *res)
{
Node dst;
Node *tmp;
tmp = temp(types[tptr]);
cgen(n->right, tmp);
gvardef(res);
dst = *res;
dst.type = types[tptr];
dst.xoffset += widthptr;
cgen(tmp, &dst);
dst.xoffset -= widthptr;
cgen(n->left, &dst);
}
void
cgen_slice(Node *n, Node *res)
{
Node src, dst, *cap, *len, *offs, *add, *base;
cap = n->list->n;
len = n->list->next->n;
offs = N;
if(n->list->next->next)
offs = n->list->next->next->n;
base = temp(types[TUINTPTR]);
if(isnil(n->left)) {
tempname(&src, n->left->type);
cgen(n->left, &src);
} else
src = *n->left;
if(n->op == OSLICE || n->op == OSLICE3 || n->op == OSLICESTR)
src.xoffset += Array_array;
if(n->op == OSLICEARR || n->op == OSLICE3ARR) {
if(!isptr[n->left->type->etype])
fatal("slicearr is supposed to work on pointer: %+N\n", n);
cgen(&src, base);
cgen_checknil(base);
if(offs != N) {
add = nod(OADD, base, offs);
typecheck(&add, Erv);
cgen(add, base);
}
} else if(offs == N) {
src.type = types[tptr];
cgen(&src, base);
} else {
src.type = types[tptr];
add = nod(OADDPTR, &src, offs);
typecheck(&add, Erv);
cgen(add, base);
}
gvardef(res);
dst = *res;
dst.xoffset += Array_array;
dst.type = types[tptr];
cgen(base, &dst);
dst = *res;
dst.xoffset += Array_nel;
dst.type = types[simtype[TUINT]];
cgen(len, &dst);
if(n->op != OSLICESTR) {
dst = *res;
dst.xoffset += Array_cap;
dst.type = types[simtype[TUINT]];
cgen(cap, &dst);
}
}
int
dotoffset(Node *n, int64 *oary, Node **nn)
{
int i;
switch(n->op) {
case ODOT:
if(n->xoffset == BADWIDTH) {
dump("bad width in dotoffset", n);
fatal("bad width in dotoffset");
}
i = dotoffset(n->left, oary, nn);
if(i > 0) {
if(oary[i-1] >= 0)
oary[i-1] += n->xoffset;
else
oary[i-1] -= n->xoffset;
break;
}
if(i < 10)
oary[i++] = n->xoffset;
break;
case ODOTPTR:
if(n->xoffset == BADWIDTH) {
dump("bad width in dotoffset", n);
fatal("bad width in dotoffset");
}
i = dotoffset(n->left, oary, nn);
if(i < 10)
oary[i++] = -(n->xoffset+1);
break;
default:
*nn = n;
return 0;
}
if(i >= 10)
*nn = N;
return i;
}
void
tempname(Node *nn, Type *t)
{
Node *n;
Sym *s;
if(curfn == N)
fatal("no curfn for tempname");
if(t == T) {
yyerror("tempname called with nil type");
t = types[TINT32];
}
snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen);
statuniqgen++;
s = lookup(namebuf);
n = nod(ONAME, N, N);
n->sym = s;
s->def = n;
n->type = t;
n->class = PAUTO;
n->addable = 1;
n->ullman = 1;
n->esc = EscNever;
n->curfn = curfn;
curfn->dcl = list(curfn->dcl, n);
dowidth(t);
n->xoffset = 0;
*nn = *n;
}
Node*
temp(Type *t)
{
Node *n;
n = nod(OXXX, N, N);
tempname(n, t);
n->sym->def->used = 1;
return n->orig;
}