ref: 3866717cbb020199d58171c1c0cdd7382a74ee82
dir: /limbo/gen.c/
#include "limbo.h"
static int addrmode[Rend] =
{
/* Rreg */ Afp,
/* Rmreg */ Amp,
/* Roff */ Aoff,
/* Rnoff */ Anoff,
/* Rdesc */ Adesc,
/* Rdescp */ Adesc,
/* Rconst */ Aimm,
/* Ralways */ Aerr,
/* Radr */ Afpind,
/* Rmadr */ Ampind,
/* Rcant */ Aerr,
/* Rpc */ Apc,
/* Rmpc */ Aerr,
/* Rareg */ Aerr,
/* Ramreg */ Aerr,
/* Raadr */ Aerr,
/* Ramadr */ Aerr,
/* Rldt */ Aldt,
};
static Decl *wtemp;
static Decl *bigtemp;
static int ntemp;
static Node retnode;
static Inst zinst;
int *blockstack;
int blockdep;
int nblocks;
static int lenblockstack;
static Node *ntoz;
static Inst* genfixop(Src *src, int op, Node *s, Node *m, Node *d);
void
genstart(void)
{
Decl *d;
d = mkdecl(&nosrc, Dlocal, tint);
d->sym = enter(".ret", 0);
d->offset = IBY2WD * REGRET;
retnode = znode;
retnode.op = Oname;
retnode.addable = Rreg;
retnode.decl = d;
retnode.ty = tint;
zinst.op = INOP;
zinst.sm = Anone;
zinst.dm = Anone;
zinst.mm = Anone;
firstinst = allocmem(sizeof *firstinst);
*firstinst = zinst;
lastinst = firstinst;
blocks = -1;
blockdep = 0;
nblocks = 0;
}
/*
* manage nested control flow blocks
*/
int
pushblock(void)
{
if(blockdep >= lenblockstack){
lenblockstack = blockdep + 32;
blockstack = reallocmem(blockstack, lenblockstack * sizeof *blockstack);
}
blockstack[blockdep++] = blocks;
return blocks = nblocks++;
}
void
repushblock(int b)
{
blockstack[blockdep++] = blocks;
blocks = b;
}
void
popblock(void)
{
blocks = blockstack[blockdep -= 1];
}
void
tinit(void)
{
wtemp = nil;
bigtemp = nil;
}
Decl*
tdecls(void)
{
Decl *d;
for(d = wtemp; d != nil; d = d->next){
if(d->tref != 1)
fatal("temporary %s has %d references", d->sym->name, d->tref-1);
}
for(d = bigtemp; d != nil; d = d->next){
if(d->tref != 1)
fatal("temporary %s has %d references", d->sym->name, d->tref-1);
}
return appdecls(wtemp, bigtemp);
}
Node*
talloc(Node *n, Type *t, Node *nok)
{
Decl *d, *ok;
Desc *desc;
char buf[StrSize];
ok = nil;
if(nok != nil)
ok = nok->decl;
if(ok == nil || ok->tref == 0 || tattr[ok->ty->kind].big != tattr[t->kind].big || ok->ty->align != t->align)
ok = nil;
*n = znode;
n->op = Oname;
n->addable = Rreg;
n->ty = t;
if(tattr[t->kind].big){
desc = mktdesc(t);
if(ok != nil && ok->desc == desc){
ok->tref++;
ok->refs++;
n->decl = ok;
return n;
}
for(d = bigtemp; d != nil; d = d->next){
if(d->tref == 1 && d->desc == desc && d->ty->align == t->align){
d->tref++;
d->refs++;
n->decl = d;
return n;
}
}
d = mkdecl(&nosrc, Dlocal, t);
d->desc = desc;
d->tref = 2;
d->refs = 1;
n->decl = d;
seprint(buf, buf+sizeof(buf), ".b%d", ntemp++);
d->sym = enter(buf, 0);
d->next = bigtemp;
bigtemp = d;
return n;
}
if(ok != nil
&& tattr[ok->ty->kind].isptr == tattr[t->kind].isptr
&& ok->ty->size == t->size){
ok->tref++;
n->decl = ok;
return n;
}
for(d = wtemp; d != nil; d = d->next){
if(d->tref == 1
&& tattr[d->ty->kind].isptr == tattr[t->kind].isptr
&& d->ty->size == t->size
&& d->ty->align == t->align){
d->tref++;
n->decl = d;
return n;
}
}
d = mkdecl(&nosrc, Dlocal, t);
d->tref = 2;
d->refs = 1;
n->decl = d;
seprint(buf, buf+sizeof(buf), ".t%d", ntemp++);
d->sym = enter(buf, 0);
d->next = wtemp;
wtemp = d;
return n;
}
void
tfree(Node *n)
{
if(n == nil || n->decl == nil || n->decl->tref == 0)
return;
if(n->decl->tref == 1)
fatal("double free of temporary %s", n->decl->sym->name);
if (--n->decl->tref == 1)
zcom1(n, nil);
}
void
tfreelater(Node *n)
{
if(n == nil || n->decl == nil || n->decl->tref == 0)
return;
if(n->decl->tref == 1)
fatal("double free of temporary %s", n->decl->sym->name);
if(--n->decl->tref == 1){
Node *nn = mkn(Oname, nil, nil);
*nn = *n;
nn->left = ntoz;
ntoz = nn;
n->decl->tref++;
}
}
void
tfreenow()
{
Node *n, *nn;
for(n = ntoz; n != nil; n = nn){
nn = n->left;
n->left = nil;
if(n->decl->tref != 2)
fatal("bad free of temporary %s", n->decl->sym->name);
--n->decl->tref;
zcom1(n, nil);
}
ntoz = nil;
}
/*
* realloc a temporary after it's been freed
*/
Node*
tacquire(Node *n)
{
if(n == nil || n->decl == nil || n->decl->tref == 0)
return n;
/*
if(n->decl->tref != 1)
fatal("tacquire ref != 1: %d", n->decl->tref);
*/
n->decl->tref++;
return n;
}
void
trelease(Node *n)
{
if(n == nil || n->decl == nil || n->decl->tref == 0)
return;
if(n->decl->tref == 1)
fatal("double release of temporary %s", n->decl->sym->name);
n->decl->tref--;
}
Inst*
mkinst(void)
{
Inst *in;
in = lastinst->next;
if(in == nil){
in = allocmem(sizeof *in);
*in = zinst;
lastinst->next = in;
}
lastinst = in;
in->block = blocks;
if(blocks < 0)
fatal("mkinst no block");
return in;
}
Inst*
nextinst(void)
{
Inst *in;
in = lastinst->next;
if(in != nil)
return in;
in = allocmem(sizeof(*in));
*in = zinst;
lastinst->next = in;
return in;
}
/*
* allocate a node for returning
*/
Node*
retalloc(Node *n, Node *nn)
{
if(nn->ty == tnone)
return nil;
*n = znode;
n->op = Oind;
n->addable = Radr;
n->left = dupn(1, &n->src, &retnode);
n->ty = nn->ty;
return n;
}
Inst*
genrawop(Src *src, int op, Node *s, Node *m, Node *d)
{
Inst *in;
in = mkinst();
in->op = op;
in->src = *src;
if(in->sm != Anone || in->mm != Anone || in->dm != Anone)
fatal("bogus mkinst in genrawop: %I\n", in);
if(s != nil){
in->s = genaddr(s);
in->sm = addrmode[s->addable];
}
if(m != nil){
in->m = genaddr(m);
in->mm = addrmode[m->addable];
if(in->mm == Ampind || in->mm == Afpind)
fatal("illegal addressing mode in register %n", m);
}
if(d != nil){
in->d = genaddr(d);
in->dm = addrmode[d->addable];
}
return in;
}
Inst*
genop(Src *src, int op, Node *s, Node *m, Node *d)
{
Inst *in;
int iop;
iop = disoptab[op][opind[d->ty->kind]];
if(iop == 0)
fatal("can't deal with op %s on %n %n %n in genop", opname[op], s, m, d);
if(iop == IMULX || iop == IDIVX)
return genfixop(src, iop, s, m, d);
in = mkinst();
in->op = iop;
in->src = *src;
if(s != nil){
in->s = genaddr(s);
in->sm = addrmode[s->addable];
}
if(m != nil){
in->m = genaddr(m);
in->mm = addrmode[m->addable];
if(in->mm == Ampind || in->mm == Afpind)
fatal("illegal addressing mode in register %n", m);
}
if(d != nil){
in->d = genaddr(d);
in->dm = addrmode[d->addable];
}
return in;
}
Inst*
genbra(Src *src, int op, Node *s, Node *m)
{
Type *t;
Inst *in;
int iop;
t = s->ty;
if(t == tany)
t = m->ty;
iop = disoptab[op][opind[t->kind]];
if(iop == 0)
fatal("can't deal with op %s on %n %n in genbra", opname[op], s, m);
in = mkinst();
in->op = iop;
in->src = *src;
if(s != nil){
in->s = genaddr(s);
in->sm = addrmode[s->addable];
}
if(m != nil){
in->m = genaddr(m);
in->mm = addrmode[m->addable];
if(in->mm == Ampind || in->mm == Afpind)
fatal("illegal addressing mode in register %n", m);
}
return in;
}
Inst*
genchan(Src *src, Node *sz, Type *mt, Node *d)
{
Inst *in;
Desc *td;
Addr reg;
int op, regm;
regm = Anone;
reg.decl = nil;
reg.reg = 0;
reg.offset = 0;
op = chantab[mt->kind];
if(op == 0)
fatal("can't deal with op %d in genchan", mt->kind);
switch(mt->kind){
case Tadt:
case Tadtpick:
case Ttuple:
td = mktdesc(mt);
if(td->nmap != 0){
op++; /* sleazy */
usedesc(td);
regm = Adesc;
reg.decl = mt->decl;
}else{
regm = Aimm;
reg.offset = mt->size;
}
break;
}
in = mkinst();
in->op = op;
in->src = *src;
in->s = reg;
in->sm = regm;
if(sz != nil){
in->m = genaddr(sz);
in->mm = addrmode[sz->addable];
}
if(d != nil){
in->d = genaddr(d);
in->dm = addrmode[d->addable];
}
return in;
}
Inst*
genmove(Src *src, int how, Type *mt, Node *s, Node *d)
{
Inst *in;
Desc *td;
Addr reg;
int op, regm;
regm = Anone;
reg.decl = nil;
reg.reg = 0;
reg.offset = 0;
op = movetab[how][mt->kind];
if(op == 0)
fatal("can't deal with op %d on %n %n in genmove", how, s, d);
switch(mt->kind){
case Tadt:
case Tadtpick:
case Ttuple:
case Texception:
if(mt->size == 0 && how == Mas)
return nil;
td = mktdesc(mt);
if(td->nmap != 0){
op++; /* sleazy */
usedesc(td);
regm = Adesc;
reg.decl = mt->decl;
}else{
regm = Aimm;
reg.offset = mt->size;
}
break;
}
in = mkinst();
in->op = op;
in->src = *src;
if(s != nil){
in->s = genaddr(s);
in->sm = addrmode[s->addable];
}
in->m = reg;
in->mm = regm;
if(d != nil){
in->d = genaddr(d);
in->dm = addrmode[d->addable];
}
if(s->addable == Rpc)
in->op = IMOVPC;
return in;
}
void
patch(Inst *b, Inst *dst)
{
Inst *n;
for(; b != nil; b = n){
n = b->branch;
b->branch = dst;
}
}
long
getpc(Inst *i)
{
if(i->pc == 0 && i != firstinst && (firstinst->op != INOOP || i != firstinst->next)){
do
i = i->next;
while(i != nil && i->pc == 0);
if(i == nil || i->pc == 0)
fatal("bad instruction in getpc");
}
return i->pc;
}
/*
* follow all possible paths from n,
* marking reached code, compressing branches, and reclaiming unreached insts
*/
void
reach(Inst *in)
{
Inst *last;
foldbranch(in);
last = in;
for(in = in->next; in != nil; in = in->next){
if(!in->reach)
last->next = in->next;
else
last = in;
}
lastinst = last;
}
/*
* follow all possible paths from n,
* marking reached code, compressing branches, and eliminating tail recursion
*/
void
foldbranch(Inst *in)
{
Inst *b, *next;
Label *lab;
int i, n;
while(in != nil && !in->reach){
in->reach = 1;
if(in->branch != nil)
while(in->branch->op == IJMP){
if(in == in->branch || in->branch == in->branch->branch)
break;
in->branch = in->branch->branch;
}
switch(in->op){
case IGOTO:
case ICASE:
case ICASEL:
case ICASEC:
case IEXC:
foldbranch(in->d.decl->ty->cse->iwild);
lab = in->d.decl->ty->cse->labs;
n = in->d.decl->ty->cse->nlab;
for(i = 0; i < n; i++)
foldbranch(lab[i].inst);
if(in->op == IEXC)
in->op = INOOP;
return;
case IEXC0:
foldbranch(in->branch);
in->op = INOOP;
break;
case IRET:
case IEXIT:
case IRAISE:
return;
case IJMP:
b = in->branch;
switch(b->op){
case ICASE:
case ICASEL:
case ICASEC:
case IRET:
case IEXIT:
next = in->next;
*in = *b;
in->next = next;
continue;
}
foldbranch(b);
return;
default:
if(in->branch != nil)
foldbranch(in->branch);
break;
}
in = in->next;
}
}
/*
* convert the addressable node into an operand
* see the comment for sumark
*/
Addr
genaddr(Node *n)
{
Addr a;
a.reg = 0;
a.offset = 0;
a.decl = nil;
if(n == nil)
return a;
switch(n->addable){
case Rreg:
if(n->decl != nil)
a.decl = n->decl;
else
a = genaddr(n->left);
break;
case Rmreg:
if(n->decl != nil)
a.decl = n->decl;
else
a = genaddr(n->left);
break;
case Rdesc:
a.decl = n->ty->decl;
break;
case Roff:
case Rnoff:
a.decl = n->decl;
break;
case Rconst:
a.offset = n->val;
break;
case Radr:
a = genaddr(n->left);
break;
case Rmadr:
a = genaddr(n->left);
break;
case Rareg:
case Ramreg:
a = genaddr(n->left);
if(n->op == Oadd)
a.reg += n->right->val;
break;
case Raadr:
case Ramadr:
a = genaddr(n->left);
if(n->op == Oadd)
a.offset += n->right->val;
break;
case Rldt:
a.decl = n->decl;
break;
case Rdescp:
case Rpc:
a.decl = n->decl;
break;
default:
fatal("can't deal with %n in genaddr", n);
break;
}
return a;
}
int
sameaddr(Node *n, Node *m)
{
Addr a, b;
if(n->addable != m->addable)
return 0;
a = genaddr(n);
b = genaddr(m);
return a.offset == b.offset && a.reg == b.reg && a.decl == b.decl;
}
long
resolvedesc(Decl *mod, long length, Decl *decls)
{
Desc *g, *d, *last;
int descid;
g = gendesc(mod, length, decls);
g->used = 0;
last = nil;
for(d = descriptors; d != nil; d = d->next){
if(!d->used){
if(last != nil)
last->next = d->next;
else
descriptors = d->next;
continue;
}
last = d;
}
g->next = descriptors;
descriptors = g;
descid = 0;
for(d = descriptors; d != nil; d = d->next)
d->id = descid++;
if(g->id != 0)
fatal("bad global descriptor id");
return descid;
}
int
resolvemod(Decl *m)
{
Decl *id, *d;
for(id = m->ty->ids; id != nil; id = id->next){
switch(id->store){
case Dfn:
id->iface->pc = id->pc;
id->iface->desc = id->desc;
if(debug['v']) print("R1: %s %p %p %p\n", id->sym->name, id, id->iface, id->pc);
break;
case Dtype:
if(id->ty->kind != Tadt)
break;
for(d = id->ty->ids; d != nil; d = d->next){
if(d->store == Dfn){
d->iface->pc = d->pc;
d->iface->desc = d->desc;
if(debug['v']) print("R2: %s %p %p %p\n", d->sym->name, d, d->iface, d->pc);
}
}
break;
}
}
/* for addiface */
for(id = m->ty->tof->ids; id != nil; id = id->next){
if(id->store == Dfn){
if(id->pc == nil)
id->pc = id->iface->pc;
if(id->desc == nil)
id->desc = id->iface->desc;
if(debug['v']) print("R3: %s %p %p %p\n", id->sym->name, id, id->iface, id->pc);
}
}
return m->ty->tof->decl->init->val;
}
/*
* place the Tiface decs in another list
*/
Decl*
resolveldts(Decl *d, Decl **dd)
{
Decl *d1, *ld1, *d2, *ld2, *n;
d1 = d2 = nil;
ld1 = ld2 = nil;
for( ; d != nil; d = n){
n = d->next;
d->next = nil;
if(d->ty->kind == Tiface){
if(d2 == nil)
d2 = d;
else
ld2->next = d;
ld2 = d;
}
else{
if(d1 == nil)
d1 = d;
else
ld1->next = d;
ld1 = d;
}
}
*dd = d2;
return d1;
}
/*
* fix up all pc's
* finalize all data offsets
* fix up instructions with offsets too large
*/
long
resolvepcs(Inst *inst)
{
Decl *d;
Inst *in;
int op;
ulong r, off;
long v, pc;
pc = 0;
for(in = inst; in != nil; in = in->next){
if(!in->reach || in->op == INOP)
fatal("unreachable pc: %I %ld", in, pc);
if(in->op == INOOP){
in->pc = pc;
continue;
}
d = in->s.decl;
if(d != nil){
if(in->sm == Adesc){
if(d->desc != nil)
in->s.offset = d->desc->id;
}else
in->s.reg += d->offset;
}
r = in->s.reg;
off = in->s.offset;
if((in->sm == Afpind || in->sm == Ampind)
&& (r >= MaxReg || off >= MaxReg))
fatal("big offset in %I\n", in);
d = in->m.decl;
if(d != nil){
if(in->mm == Adesc){
if(d->desc != nil)
in->m.offset = d->desc->id;
}else
in->m.reg += d->offset;
}
v = 0;
switch(in->mm){
case Anone:
break;
case Aimm:
case Apc:
case Adesc:
v = in->m.offset;
break;
case Aoff:
case Anoff:
v = in->m.decl->iface->offset;
break;
case Afp:
case Amp:
case Aldt:
v = in->m.reg;
if(v < 0)
v = 0x8000;
break;
default:
fatal("can't deal with %I's m mode\n", in);
break;
}
if(v > 0x7fff || v < -0x8000){
switch(in->op){
case IALT:
case IINDX:
warn(in->src.start, "possible bug: temp m too big in %I: %ld %ld %d\n", in, in->m.reg, in->m.reg, MaxReg);
rewritedestreg(in, IMOVW, RTemp);
break;
default:
op = IMOVW;
if(isbyteinst[in->op])
op = IMOVB;
in = rewritesrcreg(in, op, RTemp, pc++);
break;
}
}
d = in->d.decl;
if(d != nil){
if(in->dm == Apc)
in->d.offset = d->pc->pc;
else
in->d.reg += d->offset;
}
r = in->d.reg;
off = in->d.offset;
if((in->dm == Afpind || in->dm == Ampind)
&& (r >= MaxReg || off >= MaxReg))
fatal("big offset in %I\n", in);
in->pc = pc;
pc++;
}
for(in = inst; in != nil; in = in->next){
d = in->s.decl;
if(d != nil && in->sm == Apc)
in->s.offset = d->pc->pc;
d = in->d.decl;
if(d != nil && in->dm == Apc)
in->d.offset = d->pc->pc;
if(in->branch != nil){
in->dm = Apc;
in->d.offset = in->branch->pc;
}
}
return pc;
}
/*
* fixp up a big register constant uses as a source
* ugly: smashes the instruction
*/
Inst*
rewritesrcreg(Inst *in, int op, int treg, int pc)
{
Inst *new;
Addr a;
int am;
a = in->m;
am = in->mm;
in->mm = Afp;
in->m.reg = treg;
in->m.decl = nil;
new = allocmem(sizeof(*in));
*new = *in;
*in = zinst;
in->src = new->src;
in->next = new;
in->op = op;
in->s = a;
in->sm = am;
in->dm = Afp;
in->d.reg = treg;
in->pc = pc;
in->reach = 1;
in->block = new->block;
return new;
}
/*
* fix up a big register constant by moving to the destination
* after the instruction completes
*/
Inst*
rewritedestreg(Inst *in, int op, int treg)
{
Inst *n;
n = allocmem(sizeof(*n));
*n = zinst;
n->next = in->next;
in->next = n;
n->src = in->src;
n->op = op;
n->sm = Afp;
n->s.reg = treg;
n->d = in->m;
n->dm = in->mm;
n->reach = 1;
n->block = in->block;
in->mm = Afp;
in->m.reg = treg;
in->m.decl = nil;
return n;
}
int
instconv(Fmt *f)
{
Inst *in;
char buf[512], *p;
char *op, *comma;
in = va_arg(f->args, Inst*);
op = nil;
if(in->op < MAXDIS)
op = instname[in->op];
if(op == nil)
op = "??";
buf[0] = '\0';
if(in->op == INOP)
return fmtstrcpy(f, "\tnop");
p = seprint(buf, buf + sizeof(buf), "\t%s\t", op);
comma = "";
if(in->sm != Anone){
p = addrprint(p, buf + sizeof(buf), in->sm, &in->s);
comma = ",";
}
if(in->mm != Anone){
p = seprint(p, buf + sizeof(buf), "%s", comma);
p = addrprint(p, buf + sizeof(buf), in->mm, &in->m);
comma = ",";
}
if(in->dm != Anone){
p = seprint(p, buf + sizeof(buf), "%s", comma);
p = addrprint(p, buf + sizeof(buf), in->dm, &in->d);
}
if(asmsym && in->s.decl != nil && in->sm == Adesc)
p = seprint(p, buf+sizeof(buf), " #%D", in->s.decl);
if(0 && asmsym && in->m.decl != nil)
p = seprint(p, buf+sizeof(buf), " #%D", in->m.decl);
if(asmsym && in->d.decl != nil && in->dm == Apc)
p = seprint(p, buf+sizeof(buf), " #%D", in->d.decl);
if(asmsym)
p = seprint(p, buf+sizeof(buf), " #%U", in->src);
USED(p);
return fmtstrcpy(f, buf);
}
char*
addrprint(char *buf, char *end, int am, Addr *a)
{
switch(am){
case Anone:
return buf;
case Aimm:
case Apc:
case Adesc:
return seprint(buf, end, "$%ld", a->offset);
case Aoff:
return seprint(buf, end, "$%ld", a->decl->iface->offset);
case Anoff:
return seprint(buf, end, "-$%ld", a->decl->iface->offset);
case Afp:
return seprint(buf, end, "%ld(fp)", a->reg);
case Afpind:
return seprint(buf, end, "%ld(%ld(fp))", a->offset, a->reg);
case Amp:
return seprint(buf, end, "%ld(mp)", a->reg);
case Ampind:
return seprint(buf, end, "%ld(%ld(mp))", a->offset, a->reg);
case Aldt:
return seprint(buf, end, "$%ld", a->reg);
case Aerr:
default:
return seprint(buf, end, "%ld(%ld(?%d?))", a->offset, a->reg, am);
}
}
static void
genstore(Src *src, Node *n, int offset)
{
Decl *de;
Node d;
de = mkdecl(&nosrc, Dlocal, tint);
de->sym = nil;
de->offset = offset;
d = znode;
d.op = Oname;
d.addable = Rreg;
d.decl = de;
d.ty = tint;
genrawop(src, IMOVW, n, nil, &d);
}
static Inst*
genfixop(Src *src, int op, Node *s, Node *m, Node *d)
{
int p, a;
Node *mm;
Inst *i;
mm = m ? m: d;
op = fixop(op, mm->ty, s->ty, d->ty, &p, &a);
if(op == IMOVW){ /* just zero d */
s = sumark(mkconst(src, 0));
return genrawop(src, op, s, nil, d);
}
if(op != IMULX && op != IDIVX)
genstore(src, sumark(mkconst(src, a)), STemp);
genstore(src, sumark(mkconst(src, p)), DTemp);
i = genrawop(src, op, s, m, d);
return i;
}
Inst*
genfixcastop(Src *src, int op, Node *s, Node *d)
{
int p, a;
Node *m;
op = fixop(op, s->ty, tint, d->ty, &p, &a);
if(op == IMOVW){ /* just zero d */
s = sumark(mkconst(src, 0));
return genrawop(src, op, s, nil, d);
}
m = sumark(mkconst(src, p));
if(op != ICVTXX)
genstore(src, sumark(mkconst(src, a)), STemp);
return genrawop(src, op, s, m, d);
}