| // Copyright 2009 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. |
| |
| #include "go.h" |
| |
| /* |
| * architecture-independent object file output |
| */ |
| |
| void |
| dumpobj(void) |
| { |
| bout = Bopen(outfile, OWRITE); |
| if(bout == nil) |
| fatal("cant open %s", outfile); |
| |
| Bprint(bout, "%s\n", thestring); |
| Bprint(bout, " exports automatically generated from\n"); |
| Bprint(bout, " %s in package \"%s\"\n", curio.infile, package); |
| dumpexport(); |
| Bprint(bout, "\n!\n"); |
| |
| outhist(bout); |
| |
| // add nil plist w AEND to catch |
| // auto-generated trampolines, data |
| newplist(); |
| |
| dumpglobls(); |
| dumpsignatures(); |
| dumpdata(); |
| dumpfuncs(); |
| |
| Bterm(bout); |
| } |
| |
| void |
| dumpglobls(void) |
| { |
| Dcl *d; |
| Sym *s; |
| Node *n; |
| |
| // add globals |
| for(d=externdcl; d!=D; d=d->forw) { |
| if(d->op != ONAME) |
| continue; |
| |
| s = d->dsym; |
| if(s == S) |
| fatal("external nil"); |
| n = d->dnode; |
| if(n == N || n->type == T) |
| fatal("external %S nil\n", s); |
| |
| if(n->class == PFUNC) |
| continue; |
| |
| dowidth(n->type); |
| ggloblnod(s->def, n->type->width); |
| } |
| } |
| |
| void |
| Bputdot(Biobuf *b) |
| { |
| // put out middle dot ยท |
| Bputc(b, 0xc2); |
| Bputc(b, 0xb7); |
| } |
| |
| void |
| outhist(Biobuf *b) |
| { |
| Hist *h; |
| char *p, *q, *op; |
| int n; |
| |
| for(h = hist; h != H; h = h->link) { |
| p = h->name; |
| op = 0; |
| |
| if(p && p[0] != '/' && h->offset == 0 && pathname && pathname[0] == '/') { |
| op = p; |
| p = pathname; |
| } |
| |
| while(p) { |
| q = utfrune(p, '/'); |
| if(q) { |
| n = q-p; |
| if(n == 0) |
| n = 1; // leading "/" |
| q++; |
| } else { |
| n = strlen(p); |
| q = 0; |
| } |
| if(n) |
| zfile(b, p, n); |
| p = q; |
| if(p == 0 && op) { |
| p = op; |
| op = 0; |
| } |
| } |
| |
| zhist(b, h->line, h->offset); |
| } |
| } |
| |
| void |
| ieeedtod(uint64 *ieee, double native) |
| { |
| double fr, ho, f; |
| int exp; |
| uint32 h, l; |
| |
| if(native < 0) { |
| ieeedtod(ieee, -native); |
| *ieee |= 1ULL<<63; |
| return; |
| } |
| if(native == 0) { |
| *ieee = 0; |
| return; |
| } |
| fr = frexp(native, &exp); |
| f = 2097152L; /* shouldnt use fp constants here */ |
| fr = modf(fr*f, &ho); |
| h = ho; |
| h &= 0xfffffL; |
| h |= (exp+1022L) << 20; |
| f = 65536L; |
| fr = modf(fr*f, &ho); |
| l = ho; |
| l <<= 16; |
| l |= (int32)(fr*f); |
| *ieee = ((uint64)h << 32) | l; |
| } |
| |
| static int |
| sigcmp(Sig *a, Sig *b) |
| { |
| return strcmp(a->name, b->name); |
| } |
| |
| /* |
| * Add DATA for signature s. |
| * progt - type in program |
| * ifacet - type stored in interface (==progt if small, ==ptrto(progt) if large) |
| * rcvrt - type used as method interface. eqtype(ifacet, rcvrt) is always true, |
| * but ifacet might have a name that rcvrt does not. |
| * methodt - type with methods hanging off it (progt==*methodt sometimes) |
| * |
| * memory layout is Sigt struct from iface.c: |
| * struct Sigt |
| * { |
| * byte* name; // name of basic type |
| * Sigt* link; // for linking into hash tables |
| * uint32 thash; // hash of type |
| * uint32 mhash; // hash of methods |
| * uint16 width; // width of base type in bytes |
| * uint16 alg; // algorithm |
| * struct { |
| * byte* fname; |
| * uint32 fhash; // hash of type |
| * uint32 offset; // offset of substruct |
| * void (*fun)(void); |
| * } meth[1]; // one or more - last name is nil |
| * }; |
| */ |
| void |
| dumpsigt(Type *progt, Type *ifacet, Type *rcvrt, Type *methodt, Sym *s) |
| { |
| Type *f; |
| int o; |
| Sig *a, *b; |
| char buf[NSYMB]; |
| Type *this; |
| Prog *oldlist; |
| Sym *method; |
| uint32 sighash; |
| int ot; |
| |
| if(debug['r']) { |
| print("dumpsigt progt=%T ifacet=%T rcvrt=%T methodt=%T s=%S\n", |
| progt, ifacet, rcvrt, methodt, s); |
| } |
| |
| a = nil; |
| o = 0; |
| oldlist = nil; |
| sighash = typehash(progt, 1, 0); |
| for(f=methodt->method; f!=T; f=f->down) { |
| if(f->type->etype != TFUNC) |
| continue; |
| |
| if(f->etype != TFIELD) |
| fatal("dumpsignatures: not field"); |
| |
| method = f->sym; |
| if(method == nil) |
| continue; |
| |
| // get receiver type for this particular method. |
| this = getthisx(f->type)->type->type; |
| if(f->embedded != 2 && isptr[this->etype] && !isptr[progt->etype] && !isifacemethod(f)) { |
| // pointer receiver method but value method set. |
| // ignore. |
| if(debug['r']) |
| print("ignore %T for %T\n", f, progt); |
| continue; |
| } |
| |
| b = mal(sizeof(*b)); |
| b->link = a; |
| a = b; |
| |
| a->name = method->name; |
| a->hash = PRIME8*stringhash(a->name) + PRIME9*typehash(f->type, 0, 0); |
| if(!exportname(a->name)) |
| a->hash += PRIME10*stringhash(package); |
| a->perm = o; |
| a->sym = methodsym(method, rcvrt); |
| |
| sighash = sighash*100003 + a->hash; |
| |
| if(!a->sym->siggen) { |
| a->sym->siggen = 1; |
| |
| if(!eqtype(this, ifacet)) { |
| if(oldlist == nil) |
| oldlist = pc; |
| |
| // It would be okay to call genwrapper here always, |
| // but we can generate more efficient code |
| // using genembedtramp if all that is necessary |
| // is a pointer adjustment and a JMP. |
| if(f->embedded && isptr[ifacet->etype] && !isifacemethod(f)) |
| genembedtramp(ifacet, a); |
| else |
| genwrapper(ifacet, f, a->sym); |
| } |
| } |
| o++; |
| } |
| |
| // restore data output |
| if(oldlist) { |
| // old list ended with AEND; change to ANOP |
| // so that the trampolines that follow can be found. |
| nopout(oldlist); |
| |
| // start new data list |
| newplist(); |
| } |
| |
| a = lsort(a, sigcmp); |
| ot = 0; |
| ot = rnd(ot, maxround); // base structure |
| |
| // base of type signature contains parameters |
| snprint(buf, sizeof buf, "%#T", progt); |
| ot = dstringptr(s, ot, buf); // name |
| ot = duintptr(s, ot, 0); // skip link |
| ot = duint32(s, ot, typehash(progt, 1, 0)); // thash |
| ot = duint32(s, ot, sighash); // mhash |
| ot = duint16(s, ot, progt->width); // width |
| ot = duint16(s, ot, algtype(progt)); // algorithm |
| |
| for(b=a; b!=nil; b=b->link) { |
| ot = rnd(ot, maxround); // base of substructure |
| ot = dstringptr(s, ot, b->name); // field name |
| ot = duint32(s, ot, b->hash); // hash |
| ot = duint32(s, ot, 0); // offset |
| ot = dsymptr(s, ot, b->sym); // &method |
| } |
| |
| // nil field name at end |
| ot = rnd(ot, maxround); |
| ot = duintptr(s, ot, 0); |
| |
| // set DUPOK to allow other .6s to contain |
| // the same signature. only one will be chosen. |
| // should only happen for empty signatures |
| ggloblsym(s, ot, a == nil); |
| } |
| |
| /* |
| * memory layout is Sigi struct from iface.c: |
| * struct Sigi |
| * { |
| * byte* name; |
| * uint32 hash; |
| * uint32 size; // number of methods |
| * struct { |
| * byte* fname; |
| * uint32 fhash; |
| * uint32 perm; // location of fun in Sigt |
| * } meth[1]; // [size+1] - last name is nil |
| * }; |
| */ |
| void |
| dumpsigi(Type *t, Sym *s) |
| { |
| Type *f; |
| Sym *s1; |
| int o; |
| Sig *a, *b; |
| char buf[NSYMB]; |
| uint32 sighash; |
| int ot; |
| |
| a = nil; |
| o = 0; |
| sighash = 0; |
| for(f=t->type; f!=T; f=f->down) { |
| if(f->type->etype != TFUNC) |
| continue; |
| |
| if(f->etype != TFIELD) |
| fatal("dumpsignatures: not field"); |
| |
| s1 = f->sym; |
| if(s1 == nil) |
| continue; |
| |
| b = mal(sizeof(*b)); |
| b->link = a; |
| a = b; |
| |
| a->name = s1->name; |
| a->hash = PRIME8*stringhash(a->name) + PRIME9*typehash(f->type, 0, 0); |
| if(!exportname(a->name)) |
| a->hash += PRIME10*stringhash(package); |
| a->perm = o; |
| a->sym = methodsym(f->sym, t); |
| a->offset = 0; |
| |
| sighash = sighash*100003 + a->hash; |
| |
| o++; |
| } |
| |
| a = lsort(a, sigcmp); |
| ot = 0; |
| ot = rnd(ot, maxround); // base structure |
| |
| // sigi[0].name = type name, for runtime error message |
| snprint(buf, sizeof buf, "%#T", t); |
| ot = dstringptr(s, ot, buf); |
| |
| // first field of an interface signature |
| // contains the count and is not a real entry |
| |
| // sigi[0].hash = sighash |
| ot = duint32(s, ot, sighash); |
| |
| // sigi[0].offset = count |
| o = 0; |
| for(b=a; b!=nil; b=b->link) |
| o++; |
| ot = duint32(s, ot, o); |
| |
| for(b=a; b!=nil; b=b->link) { |
| //print(" %s\n", b->name); |
| ot = rnd(ot, maxround); // base structure |
| |
| // sigx[++].name = "fieldname" |
| // sigx[++].hash = hashcode |
| // sigi[++].perm = mapped offset of method |
| ot = dstringptr(s, ot, b->name); |
| ot = duint32(s, ot, b->hash); |
| ot = duint32(s, ot, b->perm); |
| } |
| |
| // nil field name at end |
| ot = rnd(ot, maxround); |
| ot = duintptr(s, ot, 0); |
| |
| // TODO(rsc): DUPOK should not be necessary here, |
| // and I am a bit worried that it is. If I turn it off, |
| // I get multiple definitions for sigi.dotdotdot. |
| ggloblsym(s, ot, 1); |
| } |
| |
| void |
| dumpsignatures(void) |
| { |
| int et; |
| Dcl *d, *x; |
| Type *t, *progt, *methodt, *ifacet, *rcvrt; |
| Sym *s; |
| Node *n; |
| |
| // copy externdcl list to signatlist |
| for(d=externdcl; d!=D; d=d->forw) { |
| if(d->op != OTYPE) |
| continue; |
| |
| t = d->dtype; |
| if(t == T) |
| continue; |
| |
| n = signame(t); |
| if(n == N || n->sym == S) |
| continue; |
| s = n->sym; |
| |
| x = mal(sizeof(*d)); |
| x->op = OTYPE; |
| if(t->etype == TINTER) |
| x->dtype = t; |
| else |
| x->dtype = ptrto(t); |
| x->forw = signatlist; |
| x->block = 0; |
| signatlist = x; |
| //print("SIG = %lS %lS %lT\n", d->dsym, s, t); |
| } |
| |
| // process signatlist |
| for(d=signatlist; d!=D; d=d->forw) { |
| if(d->op != OTYPE) |
| continue; |
| t = d->dtype; |
| et = t->etype; |
| n = signame(t); |
| //print("signame %S for %T\n", s, t); |
| if(n == N || n->sym == S) |
| continue; |
| s = n->sym; |
| |
| // only emit one |
| if(s->siggen) |
| continue; |
| s->siggen = 1; |
| |
| // interface is easy |
| if(et == TINTER || et == TDDD) { |
| if(t->sym && !t->local) |
| continue; |
| dumpsigi(t, s); |
| continue; |
| } |
| |
| // non-interface is more complex |
| progt = t; |
| methodt = t; |
| ifacet = t; |
| rcvrt = t; |
| |
| // if there's a pointer, methods are on base. |
| methodt = methtype(progt); |
| if(methodt == T) { |
| // if that failed, go back to progt, |
| // assuming we're writing out a signature |
| // for a type with no methods |
| methodt = progt; |
| } else { |
| expandmeth(methodt->sym, methodt); |
| } |
| |
| // if ifacet is too wide, the methods will see a pointer. |
| if(ifacet->width > widthptr) { |
| ifacet = ptrto(progt); |
| rcvrt = ptrto(progt); |
| } |
| |
| // don't emit non-trivial signatures for types defined outside this file. |
| // non-trivial signatures might also drag in generated trampolines, |
| // and ar can't handle duplicate functions. |
| // only pay attention to types with symbols, because |
| // the ... structs and maybe other internal structs |
| // don't get marked as local. |
| if(methodt->method && methodt->sym && !methodt->local) |
| continue; |
| |
| //print("s=%S\n", s); |
| dumpsigt(progt, ifacet, rcvrt, methodt, s); |
| } |
| } |
| |
| Sig* |
| lsort(Sig *l, int(*f)(Sig*, Sig*)) |
| { |
| Sig *l1, *l2, *le; |
| |
| if(l == 0 || l->link == 0) |
| return l; |
| |
| l1 = l; |
| l2 = l; |
| for(;;) { |
| l2 = l2->link; |
| if(l2 == 0) |
| break; |
| l2 = l2->link; |
| if(l2 == 0) |
| break; |
| l1 = l1->link; |
| } |
| |
| l2 = l1->link; |
| l1->link = 0; |
| l1 = lsort(l, f); |
| l2 = lsort(l2, f); |
| |
| /* set up lead element */ |
| if((*f)(l1, l2) < 0) { |
| l = l1; |
| l1 = l1->link; |
| } else { |
| l = l2; |
| l2 = l2->link; |
| } |
| le = l; |
| |
| for(;;) { |
| if(l1 == 0) { |
| while(l2) { |
| le->link = l2; |
| le = l2; |
| l2 = l2->link; |
| } |
| le->link = 0; |
| break; |
| } |
| if(l2 == 0) { |
| while(l1) { |
| le->link = l1; |
| le = l1; |
| l1 = l1->link; |
| } |
| break; |
| } |
| if((*f)(l1, l2) < 0) { |
| le->link = l1; |
| le = l1; |
| l1 = l1->link; |
| } else { |
| le->link = l2; |
| le = l2; |
| l2 = l2->link; |
| } |
| } |
| le->link = 0; |
| return l; |
| } |
| |