blob: 64d18989637e3df8075665b3486a0a7229696777 [file] [log] [blame]
// 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;
}