// Derived from Inferno utils/6c/swt.c // http://code.google.com/p/inferno-os/source/browse/utils/6c/swt.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) // Portions Copyright © 2004,2006 Bruce Ellis // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others // Portions Copyright © 2009 The Go Authors. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include #include #include "gg.h" void zname(Biobuf *b, Sym *s, int t) { Bputc(b, ANAME); /* as */ Bputc(b, ANAME>>8); /* as */ Bputc(b, t); /* type */ Bputc(b, s->sym); /* sym */ Bputname(b, s); } void zfile(Biobuf *b, char *p, int n) { Bputc(b, ANAME); Bputc(b, ANAME>>8); Bputc(b, D_FILE); Bputc(b, 1); Bputc(b, '<'); Bwrite(b, p, n); Bputc(b, 0); } void zhist(Biobuf *b, int line, vlong offset) { Addr a; Bputc(b, AHISTORY); Bputc(b, AHISTORY>>8); Bputc(b, line); Bputc(b, line>>8); Bputc(b, line>>16); Bputc(b, line>>24); zaddr(b, &zprog.from, 0, 0); a = zprog.to; if(offset != 0) { a.offset = offset; a.type = D_CONST; } zaddr(b, &a, 0, 0); } void zaddr(Biobuf *b, Addr *a, int s, int gotype) { int32 l; uint64 e; int i, t; char *n; t = 0; if(a->index != D_NONE || a->scale != 0) t |= T_INDEX; if(s != 0) t |= T_SYM; if(gotype != 0) t |= T_GOTYPE; switch(a->type) { case D_BRANCH: if(a->branch == nil) fatal("unpatched branch"); a->offset = a->branch->loc; default: t |= T_TYPE; case D_NONE: if(a->offset != 0) { t |= T_OFFSET; l = a->offset; if((vlong)l != a->offset) t |= T_64; } break; case D_FCONST: t |= T_FCONST; break; case D_SCONST: t |= T_SCONST; break; } Bputc(b, t); if(t & T_INDEX) { /* implies index, scale */ Bputc(b, a->index); Bputc(b, a->scale); } if(t & T_OFFSET) { /* implies offset */ l = a->offset; Bputc(b, l); Bputc(b, l>>8); Bputc(b, l>>16); Bputc(b, l>>24); if(t & T_64) { l = a->offset>>32; Bputc(b, l); Bputc(b, l>>8); Bputc(b, l>>16); Bputc(b, l>>24); } } if(t & T_SYM) /* implies sym */ Bputc(b, s); if(t & T_FCONST) { ieeedtod(&e, a->dval); l = e; Bputc(b, l); Bputc(b, l>>8); Bputc(b, l>>16); Bputc(b, l>>24); l = e >> 32; Bputc(b, l); Bputc(b, l>>8); Bputc(b, l>>16); Bputc(b, l>>24); return; } if(t & T_SCONST) { n = a->sval; for(i=0; itype); if(t & T_GOTYPE) Bputc(b, gotype); } static struct { struct { Sym *sym; short type; } h[NSYM]; int sym; } z; static void zsymreset(void) { for(z.sym=0; z.symsym; if(i < 0 || i >= NSYM) i = 0; if(z.h[i].type == t && z.h[i].sym == s) return i; i = z.sym; s->sym = i; zname(bout, s, t); z.h[i].sym = s; z.h[i].type = t; if(++z.sym >= NSYM) z.sym = 1; *new = 1; return i; } static int zsymaddr(Addr *a, int *new) { int t; t = a->type; if(t == D_ADDR) t = a->index; return zsym(a->sym, t, new); } void dumpfuncs(void) { Plist *pl; int sf, st, gf, gt, new; Sym *s; Prog *p; zsymreset(); // fix up pc pcloc = 0; for(pl=plist; pl!=nil; pl=pl->link) { if(isblank(pl->name)) continue; for(p=pl->firstpc; p!=P; p=p->link) { p->loc = pcloc; if(p->as != ADATA && p->as != AGLOBL) pcloc++; } } // put out functions for(pl=plist; pl!=nil; pl=pl->link) { if(isblank(pl->name)) continue; // -S prints code; -SS prints code and data if(debug['S'] && (pl->name || debug['S']>1)) { s = S; if(pl->name != N) s = pl->name->sym; print("\n--- prog list \"%S\" ---\n", s); for(p=pl->firstpc; p!=P; p=p->link) print("%P\n", p); } for(p=pl->firstpc; p!=P; p=p->link) { for(;;) { sf = zsymaddr(&p->from, &new); gf = zsym(p->from.gotype, D_EXTERN, &new); if(new && sf == gf) continue; st = zsymaddr(&p->to, &new); if(new && (st == sf || st == gf)) continue; gt = zsym(p->to.gotype, D_EXTERN, &new); if(new && (gt == sf || gt == gf || gt == st)) continue; break; } Bputc(bout, p->as); Bputc(bout, p->as>>8); Bputc(bout, p->lineno); Bputc(bout, p->lineno>>8); Bputc(bout, p->lineno>>16); Bputc(bout, p->lineno>>24); zaddr(bout, &p->from, sf, gf); zaddr(bout, &p->to, st, gt); } } } int dsname(Sym *s, int off, char *t, int n) { Prog *p; p = gins(ADATA, N, N); p->from.type = D_EXTERN; p->from.index = D_NONE; p->from.offset = off; p->from.scale = n; p->from.sym = s; p->to.type = D_SCONST; p->to.index = D_NONE; memmove(p->to.sval, t, n); return off + n; } /* * make a refer to the data s, s+len * emitting DATA if needed. */ void datastring(char *s, int len, Addr *a) { Sym *sym; sym = stringsym(s, len); a->type = D_EXTERN; a->sym = sym; a->node = sym->def; a->offset = widthptr+4; // skip header a->etype = TINT32; } /* * make a refer to the string sval, * emitting DATA if needed. */ void datagostring(Strlit *sval, Addr *a) { Sym *sym; sym = stringsym(sval->s, sval->len); a->type = D_EXTERN; a->sym = sym; a->node = sym->def; a->offset = 0; // header a->etype = TINT32; } void gdata(Node *nam, Node *nr, int wid) { Prog *p; if(nr->op == OLITERAL) { switch(nr->val.ctype) { case CTCPLX: gdatacomplex(nam, nr->val.u.cval); return; case CTSTR: gdatastring(nam, nr->val.u.sval); return; } } p = gins(ADATA, nam, nr); p->from.scale = wid; } void gdatacomplex(Node *nam, Mpcplx *cval) { Prog *p; int w; w = cplxsubtype(nam->type->etype); w = types[w]->width; p = gins(ADATA, nam, N); p->from.scale = w; p->to.type = D_FCONST; p->to.dval = mpgetflt(&cval->real); p = gins(ADATA, nam, N); p->from.scale = w; p->from.offset += w; p->to.type = D_FCONST; p->to.dval = mpgetflt(&cval->imag); } void gdatastring(Node *nam, Strlit *sval) { Prog *p; Node nod1; p = gins(ADATA, nam, N); datastring(sval->s, sval->len, &p->to); p->from.scale = types[tptr]->width; p->to.index = p->to.type; p->to.type = D_ADDR; //print("%P\n", p); nodconst(&nod1, types[TINT32], sval->len); p = gins(ADATA, nam, &nod1); p->from.scale = types[TINT32]->width; p->from.offset += types[tptr]->width; } int dstringptr(Sym *s, int off, char *str) { Prog *p; off = rnd(off, widthptr); p = gins(ADATA, N, N); p->from.type = D_EXTERN; p->from.index = D_NONE; p->from.sym = s; p->from.offset = off; p->from.scale = widthptr; datastring(str, strlen(str)+1, &p->to); p->to.index = p->to.type; p->to.type = D_ADDR; p->to.etype = TINT32; off += widthptr; return off; } int dgostrlitptr(Sym *s, int off, Strlit *lit) { Prog *p; if(lit == nil) return duintptr(s, off, 0); off = rnd(off, widthptr); p = gins(ADATA, N, N); p->from.type = D_EXTERN; p->from.index = D_NONE; p->from.sym = s; p->from.offset = off; p->from.scale = widthptr; datagostring(lit, &p->to); p->to.index = p->to.type; p->to.type = D_ADDR; p->to.etype = TINT32; off += widthptr; return off; } int dgostringptr(Sym *s, int off, char *str) { int n; Strlit *lit; if(str == nil) return duintptr(s, off, 0); n = strlen(str); lit = mal(sizeof *lit + n); strcpy(lit->s, str); lit->len = n; return dgostrlitptr(s, off, lit); } int duintxx(Sym *s, int off, uint64 v, int wid) { Prog *p; off = rnd(off, wid); p = gins(ADATA, N, N); p->from.type = D_EXTERN; p->from.index = D_NONE; p->from.sym = s; p->from.offset = off; p->from.scale = wid; p->to.type = D_CONST; p->to.index = D_NONE; p->to.offset = v; off += wid; return off; } int dsymptr(Sym *s, int off, Sym *x, int xoff) { Prog *p; off = rnd(off, widthptr); p = gins(ADATA, N, N); p->from.type = D_EXTERN; p->from.index = D_NONE; p->from.sym = s; p->from.offset = off; p->from.scale = widthptr; p->to.type = D_ADDR; p->to.index = D_EXTERN; p->to.sym = x; p->to.offset = xoff; off += widthptr; return off; } void genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) { Sym *e; int c, d, o, mov, add, loaded; Prog *p; Type *f; USED(iface); if(0 && debug['r']) print("genembedtramp %T %T %S\n", rcvr, method, newnam); e = method->sym; for(d=0; dsym); out: newplist()->name = newname(newnam); //TEXT main·S_test2(SB),7,$0 p = pc; gins(ATEXT, N, N); p->from.type = D_EXTERN; p->from.sym = newnam; p->to.type = D_CONST; p->to.offset = 0; p->from.scale = 7; //print("1. %P\n", p); mov = AMOVQ; add = AADDQ; loaded = 0; o = 0; for(c=d-1; c>=0; c--) { f = dotlist[c].field; o += f->width; if(!isptr[f->type->etype]) continue; if(!loaded) { loaded = 1; //MOVQ 8(SP), AX p = pc; gins(mov, N, N); p->from.type = D_INDIR+D_SP; p->from.offset = widthptr; p->to.type = D_AX; //print("2. %P\n", p); } //MOVQ o(AX), AX p = pc; gins(mov, N, N); p->from.type = D_INDIR+D_AX; p->from.offset = o; p->to.type = D_AX; //print("3. %P\n", p); o = 0; } if(o != 0) { //ADDQ $XX, AX p = pc; gins(add, N, N); p->from.type = D_CONST; p->from.offset = o; if(loaded) p->to.type = D_AX; else { p->to.type = D_INDIR+D_SP; p->to.offset = widthptr; } //print("4. %P\n", p); } //MOVQ AX, 8(SP) if(loaded) { p = pc; gins(mov, N, N); p->from.type = D_AX; p->to.type = D_INDIR+D_SP; p->to.offset = widthptr; //print("5. %P\n", p); } else { // TODO(rsc): obviously this is unnecessary, // but 6l has a bug, and it can't handle // JMP instructions too close to the top of // a new function. gins(ANOP, N, N); } f = dotlist[0].field; //JMP main·*Sub_test2(SB) if(isptr[f->type->etype]) f = f->type; p = pc; gins(AJMP, N, N); p->to.type = D_EXTERN; p->to.sym = methodsym(method->sym, ptrto(f->type), 0); //print("6. %P\n", p); pc->as = ARET; // overwrite AEND } void nopout(Prog *p) { p->as = ANOP; }