#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); }