blob: 8788a678bb8e5e6792069b1f110630f0fd802d57 [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 <u.h>
#include <libc.h>
#include "go.h"
#include "../ld/textflag.h"
#include "../../runtime/mgc0.h"
#include "../../runtime/typekind.h"
/*
* runtime interface and reflection data structures
*/
static NodeList* signatlist;
static Sym* dtypesym(Type*);
static Sym* weaktypesym(Type*);
static Sym* dalgsym(Type*);
static int usegcprog(Type*);
static void gengcprog(Type*, Sym**, Sym**);
static void gengcmask(Type*, uint8[16]);
static int
sigcmp(Sig *a, Sig *b)
{
int i;
i = strcmp(a->name, b->name);
if(i != 0)
return i;
if(a->pkg == b->pkg)
return 0;
if(a->pkg == nil)
return -1;
if(b->pkg == nil)
return +1;
return strcmp(a->pkg->path->s, b->pkg->path->s);
}
static 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;
}
// Builds a type respresenting a Bucket structure for
// the given map type. This type is not visible to users -
// we include only enough information to generate a correct GC
// program for it.
// Make sure this stays in sync with ../../runtime/hashmap.c!
enum {
BUCKETSIZE = 8,
MAXKEYSIZE = 128,
MAXVALSIZE = 128,
};
static Type*
mapbucket(Type *t)
{
Type *keytype, *valtype;
Type *bucket;
Type *overflowfield, *keysfield, *valuesfield;
int32 offset;
if(t->bucket != T)
return t->bucket;
keytype = t->down;
valtype = t->type;
dowidth(keytype);
dowidth(valtype);
if(keytype->width > MAXKEYSIZE)
keytype = ptrto(keytype);
if(valtype->width > MAXVALSIZE)
valtype = ptrto(valtype);
bucket = typ(TSTRUCT);
bucket->noalg = 1;
// The first field is: uint8 topbits[BUCKETSIZE].
// We don't need to encode it as GC doesn't care about it.
offset = BUCKETSIZE * 1;
keysfield = typ(TFIELD);
keysfield->type = typ(TARRAY);
keysfield->type->type = keytype;
keysfield->type->bound = BUCKETSIZE;
keysfield->type->width = BUCKETSIZE * keytype->width;
keysfield->width = offset;
keysfield->sym = mal(sizeof(Sym));
keysfield->sym->name = "keys";
offset += BUCKETSIZE * keytype->width;
valuesfield = typ(TFIELD);
valuesfield->type = typ(TARRAY);
valuesfield->type->type = valtype;
valuesfield->type->bound = BUCKETSIZE;
valuesfield->type->width = BUCKETSIZE * valtype->width;
valuesfield->width = offset;
valuesfield->sym = mal(sizeof(Sym));
valuesfield->sym->name = "values";
offset += BUCKETSIZE * valtype->width;
overflowfield = typ(TFIELD);
overflowfield->type = ptrto(bucket);
overflowfield->width = offset; // "width" is offset in structure
overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name
overflowfield->sym->name = "overflow";
offset += widthptr;
// Pad to the native integer alignment.
// This is usually the same as widthptr; the exception (as usual) is nacl/amd64.
if(widthreg > widthptr)
offset += widthreg - widthptr;
// link up fields
bucket->type = keysfield;
keysfield->down = valuesfield;
valuesfield->down = overflowfield;
overflowfield->down = T;
bucket->width = offset;
bucket->local = t->local;
t->bucket = bucket;
bucket->map = t;
return bucket;
}
// Builds a type respresenting a Hmap structure for
// the given map type. This type is not visible to users -
// we include only enough information to generate a correct GC
// program for it.
// Make sure this stays in sync with ../../runtime/hashmap.go!
static Type*
hmap(Type *t)
{
Type *h, *bucket;
Type *bucketsfield, *oldbucketsfield;
int32 offset;
if(t->hmap != T)
return t->hmap;
bucket = mapbucket(t);
h = typ(TSTRUCT);
h->noalg = 1;
offset = widthint; // count
offset += 4; // flags
offset += 4; // hash0
offset += 1; // B
offset = (offset + widthptr - 1) / widthptr * widthptr;
bucketsfield = typ(TFIELD);
bucketsfield->type = ptrto(bucket);
bucketsfield->width = offset;
bucketsfield->sym = mal(sizeof(Sym));
bucketsfield->sym->name = "buckets";
offset += widthptr;
oldbucketsfield = typ(TFIELD);
oldbucketsfield->type = ptrto(bucket);
oldbucketsfield->width = offset;
oldbucketsfield->sym = mal(sizeof(Sym));
oldbucketsfield->sym->name = "oldbuckets";
offset += widthptr;
offset += widthptr; // nevacuate (last field in Hmap)
// link up fields
h->type = bucketsfield;
bucketsfield->down = oldbucketsfield;
oldbucketsfield->down = T;
h->width = offset;
h->local = t->local;
t->hmap = h;
h->map = t;
return h;
}
Type*
hiter(Type *t)
{
int32 n, off;
Type *field[7];
Type *i;
if(t->hiter != T)
return t->hiter;
// build a struct:
// hash_iter {
// key *Key
// val *Value
// t *MapType
// h *Hmap
// buckets *Bucket
// bptr *Bucket
// other [4]uintptr
// }
// must match ../../runtime/hashmap.c:hash_iter.
field[0] = typ(TFIELD);
field[0]->type = ptrto(t->down);
field[0]->sym = mal(sizeof(Sym));
field[0]->sym->name = "key";
field[1] = typ(TFIELD);
field[1]->type = ptrto(t->type);
field[1]->sym = mal(sizeof(Sym));
field[1]->sym->name = "val";
field[2] = typ(TFIELD);
field[2]->type = ptrto(types[TUINT8]); // TODO: is there a Type type?
field[2]->sym = mal(sizeof(Sym));
field[2]->sym->name = "t";
field[3] = typ(TFIELD);
field[3]->type = ptrto(hmap(t));
field[3]->sym = mal(sizeof(Sym));
field[3]->sym->name = "h";
field[4] = typ(TFIELD);
field[4]->type = ptrto(mapbucket(t));
field[4]->sym = mal(sizeof(Sym));
field[4]->sym->name = "buckets";
field[5] = typ(TFIELD);
field[5]->type = ptrto(mapbucket(t));
field[5]->sym = mal(sizeof(Sym));
field[5]->sym->name = "bptr";
// all other non-pointer fields
field[6] = typ(TFIELD);
field[6]->type = typ(TARRAY);
field[6]->type->type = types[TUINTPTR];
field[6]->type->bound = 4;
field[6]->type->width = 4 * widthptr;
field[6]->sym = mal(sizeof(Sym));
field[6]->sym->name = "other";
// build iterator struct holding the above fields
i = typ(TSTRUCT);
i->noalg = 1;
i->type = field[0];
off = 0;
for(n = 0; n < 6; n++) {
field[n]->down = field[n+1];
field[n]->width = off;
off += field[n]->type->width;
}
field[6]->down = T;
off += field[6]->type->width;
if(off != 10 * widthptr)
yyerror("hash_iter size not correct %d %d", off, 10 * widthptr);
t->hiter = i;
i->map = t;
return i;
}
/*
* f is method type, with receiver.
* return function type, receiver as first argument (or not).
*/
Type*
methodfunc(Type *f, Type *receiver)
{
NodeList *in, *out;
Node *d;
Type *t;
in = nil;
if(receiver) {
d = nod(ODCLFIELD, N, N);
d->type = receiver;
in = list(in, d);
}
for(t=getinargx(f)->type; t; t=t->down) {
d = nod(ODCLFIELD, N, N);
d->type = t->type;
d->isddd = t->isddd;
in = list(in, d);
}
out = nil;
for(t=getoutargx(f)->type; t; t=t->down) {
d = nod(ODCLFIELD, N, N);
d->type = t->type;
out = list(out, d);
}
t = functype(N, in, out);
if(f->nname) {
// Link to name of original method function.
t->nname = f->nname;
}
return t;
}
/*
* return methods of non-interface type t, sorted by name.
* generates stub functions as needed.
*/
static Sig*
methods(Type *t)
{
Type *f, *mt, *it, *this;
Sig *a, *b;
Sym *method;
// method type
mt = methtype(t, 0);
if(mt == T)
return nil;
expandmeth(mt);
// type stored in interface word
it = t;
if(!isdirectiface(it))
it = ptrto(t);
// make list of methods for t,
// generating code if necessary.
a = nil;
for(f=mt->xmethod; f; f=f->down) {
if(f->etype != TFIELD)
fatal("methods: not field %T", f);
if (f->type->etype != TFUNC || f->type->thistuple == 0)
fatal("non-method on %T method %S %T\n", mt, f->sym, f);
if (!getthisx(f->type)->type)
fatal("receiver with no type on %T method %S %T\n", mt, f->sym, f);
if(f->nointerface)
continue;
method = f->sym;
if(method == nil)
continue;
// get receiver type for this particular method.
// if pointer receiver but non-pointer t and
// this is not an embedded pointer inside a struct,
// method does not apply.
this = getthisx(f->type)->type->type;
if(isptr[this->etype] && this->type == t)
continue;
if(isptr[this->etype] && !isptr[t->etype]
&& f->embedded != 2 && !isifacemethod(f->type))
continue;
b = mal(sizeof(*b));
b->link = a;
a = b;
a->name = method->name;
if(!exportname(method->name)) {
if(method->pkg == nil)
fatal("methods: missing package");
a->pkg = method->pkg;
}
a->isym = methodsym(method, it, 1);
a->tsym = methodsym(method, t, 0);
a->type = methodfunc(f->type, t);
a->mtype = methodfunc(f->type, nil);
if(!(a->isym->flags & SymSiggen)) {
a->isym->flags |= SymSiggen;
if(!eqtype(this, it) || this->width < types[tptr]->width) {
compiling_wrappers = 1;
genwrapper(it, f, a->isym, 1);
compiling_wrappers = 0;
}
}
if(!(a->tsym->flags & SymSiggen)) {
a->tsym->flags |= SymSiggen;
if(!eqtype(this, t)) {
compiling_wrappers = 1;
genwrapper(t, f, a->tsym, 0);
compiling_wrappers = 0;
}
}
}
return lsort(a, sigcmp);
}
/*
* return methods of interface type t, sorted by name.
*/
static Sig*
imethods(Type *t)
{
Sig *a, *all, *last;
Type *f;
Sym *method, *isym;
all = nil;
last = nil;
for(f=t->type; f; f=f->down) {
if(f->etype != TFIELD)
fatal("imethods: not field");
if(f->type->etype != TFUNC || f->sym == nil)
continue;
method = f->sym;
a = mal(sizeof(*a));
a->name = method->name;
if(!exportname(method->name)) {
if(method->pkg == nil)
fatal("imethods: missing package");
a->pkg = method->pkg;
}
a->mtype = f->type;
a->offset = 0;
a->type = methodfunc(f->type, nil);
if(last && sigcmp(last, a) >= 0)
fatal("sigcmp vs sortinter %s %s", last->name, a->name);
if(last == nil)
all = a;
else
last->link = a;
last = a;
// Compiler can only refer to wrappers for non-blank methods.
if(isblanksym(method))
continue;
// NOTE(rsc): Perhaps an oversight that
// IfaceType.Method is not in the reflect data.
// Generate the method body, so that compiled
// code can refer to it.
isym = methodsym(method, t, 0);
if(!(isym->flags & SymSiggen)) {
isym->flags |= SymSiggen;
genwrapper(t, f, isym, 0);
}
}
return all;
}
static void
dimportpath(Pkg *p)
{
static Pkg *gopkg;
char *nam;
Node *n;
if(p->pathsym != S)
return;
if(gopkg == nil) {
gopkg = mkpkg(strlit("go"));
gopkg->name = "go";
}
nam = smprint("importpath.%s.", p->prefix);
n = nod(ONAME, N, N);
n->sym = pkglookup(nam, gopkg);
free(nam);
n->class = PEXTERN;
n->xoffset = 0;
p->pathsym = n->sym;
gdatastring(n, p->path);
ggloblsym(n->sym, types[TSTRING]->width, DUPOK|RODATA);
}
static int
dgopkgpath(Sym *s, int ot, Pkg *pkg)
{
if(pkg == nil)
return dgostringptr(s, ot, nil);
// Emit reference to go.importpath.""., which 6l will
// rewrite using the correct import path. Every package
// that imports this one directly defines the symbol.
if(pkg == localpkg) {
static Sym *ns;
if(ns == nil)
ns = pkglookup("importpath.\"\".", mkpkg(strlit("go")));
return dsymptr(s, ot, ns, 0);
}
dimportpath(pkg);
return dsymptr(s, ot, pkg->pathsym, 0);
}
/*
* uncommonType
* ../../runtime/type.go:/uncommonType
*/
static int
dextratype(Sym *sym, int off, Type *t, int ptroff)
{
int ot, n;
Sym *s;
Sig *a, *m;
m = methods(t);
if(t->sym == nil && m == nil)
return off;
// fill in *extraType pointer in header
off = rnd(off, widthptr);
dsymptr(sym, ptroff, sym, off);
n = 0;
for(a=m; a; a=a->link) {
dtypesym(a->type);
n++;
}
ot = off;
s = sym;
if(t->sym) {
ot = dgostringptr(s, ot, t->sym->name);
if(t != types[t->etype] && t != errortype)
ot = dgopkgpath(s, ot, t->sym->pkg);
else
ot = dgostringptr(s, ot, nil);
} else {
ot = dgostringptr(s, ot, nil);
ot = dgostringptr(s, ot, nil);
}
// slice header
ot = dsymptr(s, ot, s, ot + widthptr + 2*widthint);
ot = duintxx(s, ot, n, widthint);
ot = duintxx(s, ot, n, widthint);
// methods
for(a=m; a; a=a->link) {
// method
// ../../runtime/type.go:/method
ot = dgostringptr(s, ot, a->name);
ot = dgopkgpath(s, ot, a->pkg);
ot = dsymptr(s, ot, dtypesym(a->mtype), 0);
ot = dsymptr(s, ot, dtypesym(a->type), 0);
if(a->isym)
ot = dsymptr(s, ot, a->isym, 0);
else
ot = duintptr(s, ot, 0);
if(a->tsym)
ot = dsymptr(s, ot, a->tsym, 0);
else
ot = duintptr(s, ot, 0);
}
return ot;
}
static int
kinds[] =
{
[TINT] = KindInt,
[TUINT] = KindUint,
[TINT8] = KindInt8,
[TUINT8] = KindUint8,
[TINT16] = KindInt16,
[TUINT16] = KindUint16,
[TINT32] = KindInt32,
[TUINT32] = KindUint32,
[TINT64] = KindInt64,
[TUINT64] = KindUint64,
[TUINTPTR] = KindUintptr,
[TFLOAT32] = KindFloat32,
[TFLOAT64] = KindFloat64,
[TBOOL] = KindBool,
[TSTRING] = KindString,
[TPTR32] = KindPtr,
[TPTR64] = KindPtr,
[TSTRUCT] = KindStruct,
[TINTER] = KindInterface,
[TCHAN] = KindChan,
[TMAP] = KindMap,
[TARRAY] = KindArray,
[TFUNC] = KindFunc,
[TCOMPLEX64] = KindComplex64,
[TCOMPLEX128] = KindComplex128,
[TUNSAFEPTR] = KindUnsafePointer,
};
int
haspointers(Type *t)
{
Type *t1;
int ret;
if(t->haspointers != 0)
return t->haspointers - 1;
switch(t->etype) {
case TINT:
case TUINT:
case TINT8:
case TUINT8:
case TINT16:
case TUINT16:
case TINT32:
case TUINT32:
case TINT64:
case TUINT64:
case TUINTPTR:
case TFLOAT32:
case TFLOAT64:
case TCOMPLEX64:
case TCOMPLEX128:
case TBOOL:
ret = 0;
break;
case TARRAY:
if(t->bound < 0) { // slice
ret = 1;
break;
}
if(t->bound == 0) { // empty array
ret = 0;
break;
}
ret = haspointers(t->type);
break;
case TSTRUCT:
ret = 0;
for(t1=t->type; t1!=T; t1=t1->down) {
if(haspointers(t1->type)) {
ret = 1;
break;
}
}
break;
case TSTRING:
case TPTR32:
case TPTR64:
case TUNSAFEPTR:
case TINTER:
case TCHAN:
case TMAP:
case TFUNC:
default:
ret = 1;
break;
}
t->haspointers = 1+ret;
return ret;
}
/*
* commonType
* ../../runtime/type.go:/commonType
*/
static int
dcommontype(Sym *s, int ot, Type *t)
{
int i, alg, sizeofAlg, gcprog;
Sym *sptr, *algsym, *zero, *gcprog0, *gcprog1, *sbits;
uint8 gcmask[16];
static Sym *algarray;
uint64 x1, x2;
char *p;
if(ot != 0)
fatal("dcommontype %d", ot);
sizeofAlg = 2*widthptr;
if(algarray == nil)
algarray = pkglookup("algarray", runtimepkg);
dowidth(t);
alg = algtype(t);
algsym = S;
if(alg < 0)
algsym = dalgsym(t);
if(t->sym != nil && !isptr[t->etype])
sptr = dtypesym(ptrto(t));
else
sptr = weaktypesym(ptrto(t));
// All (non-reflect-allocated) Types share the same zero object.
// Each place in the compiler where a pointer to the zero object
// might be returned by a runtime call (map access return value,
// 2-arg type cast) declares the size of the zerovalue it needs.
// The linker magically takes the max of all the sizes.
zero = pkglookup("zerovalue", runtimepkg);
// We use size 0 here so we get the pointer to the zero value,
// but don't allocate space for the zero value unless we need it.
// TODO: how do we get this symbol into bss? We really want
// a read-only bss, but I don't think such a thing exists.
// ../../pkg/reflect/type.go:/^type.commonType
// actual type structure
// type commonType struct {
// size uintptr
// hash uint32
// _ uint8
// align uint8
// fieldAlign uint8
// kind uint8
// alg unsafe.Pointer
// gc unsafe.Pointer
// string *string
// *extraType
// ptrToThis *Type
// zero unsafe.Pointer
// }
ot = duintptr(s, ot, t->width);
ot = duint32(s, ot, typehash(t));
ot = duint8(s, ot, 0); // unused
// runtime (and common sense) expects alignment to be a power of two.
i = t->align;
if(i == 0)
i = 1;
if((i&(i-1)) != 0)
fatal("invalid alignment %d for %T", t->align, t);
ot = duint8(s, ot, t->align); // align
ot = duint8(s, ot, t->align); // fieldAlign
gcprog = usegcprog(t);
i = kinds[t->etype];
if(t->etype == TARRAY && t->bound < 0)
i = KindSlice;
if(!haspointers(t))
i |= KindNoPointers;
if(isdirectiface(t))
i |= KindDirectIface;
if(gcprog)
i |= KindGCProg;
ot = duint8(s, ot, i); // kind
if(alg >= 0)
ot = dsymptr(s, ot, algarray, alg*sizeofAlg);
else
ot = dsymptr(s, ot, algsym, 0);
// gc
if(gcprog) {
gengcprog(t, &gcprog0, &gcprog1);
if(gcprog0 != S)
ot = dsymptr(s, ot, gcprog0, 0);
else
ot = duintptr(s, ot, 0);
ot = dsymptr(s, ot, gcprog1, 0);
} else {
gengcmask(t, gcmask);
x1 = 0;
for(i=0; i<8; i++)
x1 = x1<<8 | gcmask[i];
if(widthptr == 4) {
p = smprint("gcbits.%#016llux", x1);
} else {
x2 = 0;
for(i=0; i<8; i++)
x2 = x2<<8 | gcmask[i+8];
p = smprint("gcbits.%#016llux%016llux", x1, x2);
}
sbits = pkglookup(p, runtimepkg);
if((sbits->flags & SymUniq) == 0) {
sbits->flags |= SymUniq;
for(i = 0; i < 2*widthptr; i++)
duint8(sbits, i, gcmask[i]);
ggloblsym(sbits, 2*widthptr, DUPOK|RODATA);
}
ot = dsymptr(s, ot, sbits, 0);
ot = duintptr(s, ot, 0);
}
p = smprint("%-uT", t);
//print("dcommontype: %s\n", p);
ot = dgostringptr(s, ot, p); // string
free(p);
// skip pointer to extraType,
// which follows the rest of this type structure.
// caller will fill in if needed.
// otherwise linker will assume 0.
ot += widthptr;
ot = dsymptr(s, ot, sptr, 0); // ptrto type
ot = dsymptr(s, ot, zero, 0); // ptr to zero value
return ot;
}
Sym*
typesym(Type *t)
{
char *p;
Sym *s;
p = smprint("%-T", t);
s = pkglookup(p, typepkg);
//print("typesym: %s -> %+S\n", p, s);
free(p);
return s;
}
Sym*
tracksym(Type *t)
{
char *p;
Sym *s;
p = smprint("%-T.%s", t->outer, t->sym->name);
s = pkglookup(p, trackpkg);
free(p);
return s;
}
Sym*
typelinksym(Type *t)
{
char *p;
Sym *s;
// %-uT is what the generated Type's string field says.
// It uses (ambiguous) package names instead of import paths.
// %-T is the complete, unambiguous type name.
// We want the types to end up sorted by string field,
// so use that first in the name, and then add :%-T to
// disambiguate. The names are a little long but they are
// discarded by the linker and do not end up in the symbol
// table of the final binary.
p = smprint("%-uT/%-T", t, t);
s = pkglookup(p, typelinkpkg);
//print("typelinksym: %s -> %+S\n", p, s);
free(p);
return s;
}
Sym*
typesymprefix(char *prefix, Type *t)
{
char *p;
Sym *s;
p = smprint("%s.%-T", prefix, t);
s = pkglookup(p, typepkg);
//print("algsym: %s -> %+S\n", p, s);
free(p);
return s;
}
Sym*
typenamesym(Type *t)
{
Sym *s;
Node *n;
if(t == T || (isptr[t->etype] && t->type == T) || isideal(t))
fatal("typename %T", t);
s = typesym(t);
if(s->def == N) {
n = nod(ONAME, N, N);
n->sym = s;
n->type = types[TUINT8];
n->addable = 1;
n->ullman = 1;
n->class = PEXTERN;
n->xoffset = 0;
n->typecheck = 1;
s->def = n;
signatlist = list(signatlist, typenod(t));
}
return s->def->sym;
}
Node*
typename(Type *t)
{
Sym *s;
Node *n;
s = typenamesym(t);
n = nod(OADDR, s->def, N);
n->type = ptrto(s->def->type);
n->addable = 1;
n->ullman = 2;
n->typecheck = 1;
return n;
}
static Sym*
weaktypesym(Type *t)
{
char *p;
Sym *s;
p = smprint("%-T", t);
s = pkglookup(p, weaktypepkg);
//print("weaktypesym: %s -> %+S\n", p, s);
free(p);
return s;
}
static Sym*
dtypesym(Type *t)
{
int ot, xt, n, isddd, dupok;
Sym *s, *s1, *s2, *s3, *s4, *slink;
Sig *a, *m;
Type *t1, *tbase, *t2;
// Replace byte, rune aliases with real type.
// They've been separate internally to make error messages
// better, but we have to merge them in the reflect tables.
if(t == bytetype || t == runetype)
t = types[t->etype];
if(isideal(t))
fatal("dtypesym %T", t);
s = typesym(t);
if(s->flags & SymSiggen)
return s;
s->flags |= SymSiggen;
// special case (look for runtime below):
// when compiling package runtime,
// emit the type structures for int, float, etc.
tbase = t;
if(isptr[t->etype] && t->sym == S && t->type->sym != S)
tbase = t->type;
dupok = 0;
if(tbase->sym == S)
dupok = DUPOK;
if(compiling_runtime &&
(tbase == types[tbase->etype] ||
tbase == bytetype ||
tbase == runetype ||
tbase == errortype)) { // int, float, etc
goto ok;
}
// named types from other files are defined only by those files
if(tbase->sym && !tbase->local)
return s;
if(isforw[tbase->etype])
return s;
ok:
ot = 0;
xt = 0;
switch(t->etype) {
default:
ot = dcommontype(s, ot, t);
xt = ot - 3*widthptr;
break;
case TARRAY:
if(t->bound >= 0) {
// ../../runtime/type.go:/ArrayType
s1 = dtypesym(t->type);
t2 = typ(TARRAY);
t2->type = t->type;
t2->bound = -1; // slice
s2 = dtypesym(t2);
ot = dcommontype(s, ot, t);
xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0);
ot = dsymptr(s, ot, s2, 0);
ot = duintptr(s, ot, t->bound);
} else {
// ../../runtime/type.go:/SliceType
s1 = dtypesym(t->type);
ot = dcommontype(s, ot, t);
xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0);
}
break;
case TCHAN:
// ../../runtime/type.go:/ChanType
s1 = dtypesym(t->type);
ot = dcommontype(s, ot, t);
xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0);
ot = duintptr(s, ot, t->chan);
break;
case TFUNC:
for(t1=getthisx(t)->type; t1; t1=t1->down)
dtypesym(t1->type);
isddd = 0;
for(t1=getinargx(t)->type; t1; t1=t1->down) {
isddd = t1->isddd;
dtypesym(t1->type);
}
for(t1=getoutargx(t)->type; t1; t1=t1->down)
dtypesym(t1->type);
ot = dcommontype(s, ot, t);
xt = ot - 3*widthptr;
ot = duint8(s, ot, isddd);
// two slice headers: in and out.
ot = rnd(ot, widthptr);
ot = dsymptr(s, ot, s, ot+2*(widthptr+2*widthint));
n = t->thistuple + t->intuple;
ot = duintxx(s, ot, n, widthint);
ot = duintxx(s, ot, n, widthint);
ot = dsymptr(s, ot, s, ot+1*(widthptr+2*widthint)+n*widthptr);
ot = duintxx(s, ot, t->outtuple, widthint);
ot = duintxx(s, ot, t->outtuple, widthint);
// slice data
for(t1=getthisx(t)->type; t1; t1=t1->down, n++)
ot = dsymptr(s, ot, dtypesym(t1->type), 0);
for(t1=getinargx(t)->type; t1; t1=t1->down, n++)
ot = dsymptr(s, ot, dtypesym(t1->type), 0);
for(t1=getoutargx(t)->type; t1; t1=t1->down, n++)
ot = dsymptr(s, ot, dtypesym(t1->type), 0);
break;
case TINTER:
m = imethods(t);
n = 0;
for(a=m; a; a=a->link) {
dtypesym(a->type);
n++;
}
// ../../runtime/type.go:/InterfaceType
ot = dcommontype(s, ot, t);
xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s, ot+widthptr+2*widthint);
ot = duintxx(s, ot, n, widthint);
ot = duintxx(s, ot, n, widthint);
for(a=m; a; a=a->link) {
// ../../runtime/type.go:/imethod
ot = dgostringptr(s, ot, a->name);
ot = dgopkgpath(s, ot, a->pkg);
ot = dsymptr(s, ot, dtypesym(a->type), 0);
}
break;
case TMAP:
// ../../runtime/type.go:/MapType
s1 = dtypesym(t->down);
s2 = dtypesym(t->type);
s3 = dtypesym(mapbucket(t));
s4 = dtypesym(hmap(t));
ot = dcommontype(s, ot, t);
xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0);
ot = dsymptr(s, ot, s2, 0);
ot = dsymptr(s, ot, s3, 0);
ot = dsymptr(s, ot, s4, 0);
if(t->down->width > MAXKEYSIZE) {
ot = duint8(s, ot, widthptr);
ot = duint8(s, ot, 1); // indirect
} else {
ot = duint8(s, ot, t->down->width);
ot = duint8(s, ot, 0); // not indirect
}
if(t->type->width > MAXVALSIZE) {
ot = duint8(s, ot, widthptr);
ot = duint8(s, ot, 1); // indirect
} else {
ot = duint8(s, ot, t->type->width);
ot = duint8(s, ot, 0); // not indirect
}
ot = duint16(s, ot, mapbucket(t)->width);
break;
case TPTR32:
case TPTR64:
if(t->type->etype == TANY) {
// ../../runtime/type.go:/UnsafePointerType
ot = dcommontype(s, ot, t);
break;
}
// ../../runtime/type.go:/PtrType
s1 = dtypesym(t->type);
ot = dcommontype(s, ot, t);
xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0);
break;
case TSTRUCT:
// ../../runtime/type.go:/StructType
// for security, only the exported fields.
n = 0;
for(t1=t->type; t1!=T; t1=t1->down) {
dtypesym(t1->type);
n++;
}
ot = dcommontype(s, ot, t);
xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s, ot+widthptr+2*widthint);
ot = duintxx(s, ot, n, widthint);
ot = duintxx(s, ot, n, widthint);
for(t1=t->type; t1!=T; t1=t1->down) {
// ../../runtime/type.go:/structField
if(t1->sym && !t1->embedded) {
ot = dgostringptr(s, ot, t1->sym->name);
if(exportname(t1->sym->name))
ot = dgostringptr(s, ot, nil);
else
ot = dgopkgpath(s, ot, t1->sym->pkg);
} else {
ot = dgostringptr(s, ot, nil);
if(t1->type->sym != S && t1->type->sym->pkg == builtinpkg)
ot = dgopkgpath(s, ot, localpkg);
else
ot = dgostringptr(s, ot, nil);
}
ot = dsymptr(s, ot, dtypesym(t1->type), 0);
ot = dgostrlitptr(s, ot, t1->note);
ot = duintptr(s, ot, t1->width); // field offset
}
break;
}
ot = dextratype(s, ot, t, xt);
ggloblsym(s, ot, dupok|RODATA);
// generate typelink.foo pointing at s = type.foo.
// The linker will leave a table of all the typelinks for
// types in the binary, so reflect can find them.
// We only need the link for unnamed composites that
// we want be able to find.
if(t->sym == S) {
switch(t->etype) {
case TARRAY:
case TCHAN:
case TMAP:
slink = typelinksym(t);
dsymptr(slink, 0, s, 0);
ggloblsym(slink, widthptr, dupok|RODATA);
}
}
return s;
}
void
dumptypestructs(void)
{
int i;
NodeList *l;
Node *n;
Type *t;
Pkg *p;
// copy types from externdcl list to signatlist
for(l=externdcl; l; l=l->next) {
n = l->n;
if(n->op != OTYPE)
continue;
signatlist = list(signatlist, n);
}
// process signatlist
for(l=signatlist; l; l=l->next) {
n = l->n;
if(n->op != OTYPE)
continue;
t = n->type;
dtypesym(t);
if(t->sym)
dtypesym(ptrto(t));
}
// generate import strings for imported packages
for(i=0; i<nelem(phash); i++)
for(p=phash[i]; p; p=p->link)
if(p->direct)
dimportpath(p);
// do basic types if compiling package runtime.
// they have to be in at least one package,
// and runtime is always loaded implicitly,
// so this is as good as any.
// another possible choice would be package main,
// but using runtime means fewer copies in .6 files.
if(compiling_runtime) {
for(i=1; i<=TBOOL; i++)
dtypesym(ptrto(types[i]));
dtypesym(ptrto(types[TSTRING]));
dtypesym(ptrto(types[TUNSAFEPTR]));
// emit type structs for error and func(error) string.
// The latter is the type of an auto-generated wrapper.
dtypesym(ptrto(errortype));
dtypesym(functype(nil,
list1(nod(ODCLFIELD, N, typenod(errortype))),
list1(nod(ODCLFIELD, N, typenod(types[TSTRING])))));
// add paths for runtime and main, which 6l imports implicitly.
dimportpath(runtimepkg);
if(flag_race)
dimportpath(racepkg);
dimportpath(mkpkg(strlit("main")));
}
}
static Sym*
dalgsym(Type *t)
{
int ot;
Sym *s, *hash, *hashfunc, *eq, *eqfunc;
// dalgsym is only called for a type that needs an algorithm table,
// which implies that the type is comparable (or else it would use ANOEQ).
s = typesymprefix(".alg", t);
hash = typesymprefix(".hash", t);
genhash(hash, t);
eq = typesymprefix(".eq", t);
geneq(eq, t);
// make Go funcs (closures) for calling hash and equal from Go
hashfunc = typesymprefix(".hashfunc", t);
dsymptr(hashfunc, 0, hash, 0);
ggloblsym(hashfunc, widthptr, DUPOK|RODATA);
eqfunc = typesymprefix(".eqfunc", t);
dsymptr(eqfunc, 0, eq, 0);
ggloblsym(eqfunc, widthptr, DUPOK|RODATA);
// ../../runtime/alg.go:/typeAlg
ot = 0;
ot = dsymptr(s, ot, hashfunc, 0);
ot = dsymptr(s, ot, eqfunc, 0);
ggloblsym(s, ot, DUPOK|RODATA);
return s;
}
static int
usegcprog(Type *t)
{
vlong size, nptr;
if(!haspointers(t))
return 0;
if(t->width == BADWIDTH)
dowidth(t);
// Calculate size of the unrolled GC mask.
nptr = (t->width+widthptr-1)/widthptr;
size = nptr;
if(size%2)
size *= 2; // repeated
size = size*gcBits/8; // 4 bits per word
// Decide whether to use unrolled GC mask or GC program.
// We could use a more elaborate condition, but this seems to work well in practice.
// For small objects GC program can't give significant reduction.
// While large objects usually contain arrays; and even if it don't
// the program uses 2-bits per word while mask uses 4-bits per word,
// so the program is still smaller.
return size > 2*widthptr;
}
// Generates sparse GC bitmask (4 bits per word).
static void
gengcmask(Type *t, uint8 gcmask[16])
{
Bvec *vec;
vlong xoffset, nptr, i, j;
int half, mw;
uint8 bits, *pos;
memset(gcmask, 0, 16);
if(!haspointers(t))
return;
// Generate compact mask as stacks use.
xoffset = 0;
vec = bvalloc(2*widthptr*8);
twobitwalktype1(t, &xoffset, vec);
// Unfold the mask for the GC bitmap format:
// 4 bits per word, 2 high bits encode pointer info.
pos = (uint8*)gcmask;
nptr = (t->width+widthptr-1)/widthptr;
half = 0;
mw = 0;
// If number of words is odd, repeat the mask.
// This makes simpler handling of arrays in runtime.
for(j=0; j<=(nptr%2); j++) {
for(i=0; i<nptr; i++) {
bits = bvget(vec, i*BitsPerPointer) | bvget(vec, i*BitsPerPointer+1)<<1;
// Some fake types (e.g. Hmap) has missing fileds.
// twobitwalktype1 generates BitsDead for that holes,
// replace BitsDead with BitsScalar.
if(!mw && bits == BitsDead)
bits = BitsScalar;
mw = !mw && bits == BitsMultiWord;
bits <<= 2;
if(half)
bits <<= 4;
*pos |= bits;
half = !half;
if(!half)
pos++;
}
}
}
// Helper object for generation of GC programs.
typedef struct ProgGen ProgGen;
struct ProgGen
{
Sym* s;
int32 datasize;
uint8 data[256/PointersPerByte];
vlong ot;
};
static void
proggeninit(ProgGen *g, Sym *s)
{
g->s = s;
g->datasize = 0;
g->ot = 0;
memset(g->data, 0, sizeof(g->data));
}
static void
proggenemit(ProgGen *g, uint8 v)
{
g->ot = duint8(g->s, g->ot, v);
}
// Emits insData block from g->data.
static void
proggendataflush(ProgGen *g)
{
int32 i, s;
if(g->datasize == 0)
return;
proggenemit(g, insData);
proggenemit(g, g->datasize);
s = (g->datasize + PointersPerByte - 1)/PointersPerByte;
for(i = 0; i < s; i++)
proggenemit(g, g->data[i]);
g->datasize = 0;
memset(g->data, 0, sizeof(g->data));
}
static void
proggendata(ProgGen *g, uint8 d)
{
g->data[g->datasize/PointersPerByte] |= d << ((g->datasize%PointersPerByte)*BitsPerPointer);
g->datasize++;
if(g->datasize == 255)
proggendataflush(g);
}
// Skip v bytes due to alignment, etc.
static void
proggenskip(ProgGen *g, vlong off, vlong v)
{
vlong i;
for(i = off; i < off+v; i++) {
if((i%widthptr) == 0)
proggendata(g, BitsScalar);
}
}
// Emit insArray instruction.
static void
proggenarray(ProgGen *g, vlong len)
{
int32 i;
proggendataflush(g);
proggenemit(g, insArray);
for(i = 0; i < widthptr; i++, len >>= 8)
proggenemit(g, len);
}
static void
proggenarrayend(ProgGen *g)
{
proggendataflush(g);
proggenemit(g, insArrayEnd);
}
static vlong
proggenfini(ProgGen *g)
{
proggendataflush(g);
proggenemit(g, insEnd);
return g->ot;
}
static void gengcprog1(ProgGen *g, Type *t, vlong *xoffset);
// Generates GC program for large types.
static void
gengcprog(Type *t, Sym **pgc0, Sym **pgc1)
{
Sym *gc0, *gc1;
vlong nptr, size, ot, xoffset;
ProgGen g;
nptr = (t->width+widthptr-1)/widthptr;
size = nptr;
if(size%2)
size *= 2; // repeated twice
size = size*PointersPerByte/8; // 4 bits per word
size++; // unroll flag in the beginning, used by runtime (see runtime.markallocated)
// emity space in BSS for unrolled program
*pgc0 = S;
// Don't generate it if it's too large, runtime will unroll directly into GC bitmap.
if(size <= MaxGCMask) {
gc0 = typesymprefix(".gc", t);
ggloblsym(gc0, size, DUPOK|NOPTR);
*pgc0 = gc0;
}
// program in RODATA
gc1 = typesymprefix(".gcprog", t);
proggeninit(&g, gc1);
xoffset = 0;
gengcprog1(&g, t, &xoffset);
ot = proggenfini(&g);
ggloblsym(gc1, ot, DUPOK|RODATA);
*pgc1 = gc1;
}
// Recursively walks type t and writes GC program into g.
static void
gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
{
vlong fieldoffset, i, o, n;
Type *t1;
switch(t->etype) {
case TINT8:
case TUINT8:
case TINT16:
case TUINT16:
case TINT32:
case TUINT32:
case TINT64:
case TUINT64:
case TINT:
case TUINT:
case TUINTPTR:
case TBOOL:
case TFLOAT32:
case TFLOAT64:
case TCOMPLEX64:
case TCOMPLEX128:
proggenskip(g, *xoffset, t->width);
*xoffset += t->width;
break;
case TPTR32:
case TPTR64:
case TUNSAFEPTR:
case TFUNC:
case TCHAN:
case TMAP:
proggendata(g, BitsPointer);
*xoffset += t->width;
break;
case TSTRING:
proggendata(g, BitsPointer);
proggendata(g, BitsScalar);
*xoffset += t->width;
break;
case TINTER:
proggendata(g, BitsMultiWord);
if(isnilinter(t))
proggendata(g, BitsEface);
else
proggendata(g, BitsIface);
*xoffset += t->width;
break;
case TARRAY:
if(isslice(t)) {
proggendata(g, BitsPointer);
proggendata(g, BitsScalar);
proggendata(g, BitsScalar);
} else {
t1 = t->type;
if(t1->width == 0) {
// ignore
} if(t->bound <= 1 || t->bound*t1->width < 32*widthptr) {
for(i = 0; i < t->bound; i++)
gengcprog1(g, t1, xoffset);
} else if(!haspointers(t1)) {
n = t->width;
n -= -*xoffset&(widthptr-1); // skip to next ptr boundary
proggenarray(g, (n+widthptr-1)/widthptr);
proggendata(g, BitsScalar);
proggenarrayend(g);
*xoffset -= (n+widthptr-1)/widthptr*widthptr - t->width;
} else {
proggenarray(g, t->bound);
gengcprog1(g, t1, xoffset);
*xoffset += (t->bound-1)*t1->width;
proggenarrayend(g);
}
}
break;
case TSTRUCT:
o = 0;
for(t1 = t->type; t1 != T; t1 = t1->down) {
fieldoffset = t1->width;
proggenskip(g, *xoffset, fieldoffset - o);
*xoffset += fieldoffset - o;
gengcprog1(g, t1->type, xoffset);
o = fieldoffset + t1->type->width;
}
proggenskip(g, *xoffset, t->width - o);
*xoffset += t->width - o;
break;
default:
fatal("gengcprog1: unexpected type, %T", t);
}
}