root/src/cmd/gc/reflect.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sigcmp
  2. lsort
  3. mapbucket
  4. hmap
  5. hiter
  6. methodfunc
  7. methods
  8. imethods
  9. dimportpath
  10. dgopkgpath
  11. dextratype
  12. haspointers
  13. dcommontype
  14. typesym
  15. tracksym
  16. typelinksym
  17. typesymprefix
  18. typenamesym
  19. typename
  20. weaktypesym
  21. dtypesym
  22. dumptypestructs
  23. dalgsym
  24. gcinline
  25. dgcsym1
  26. dgcsym

// 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 "../../pkg/runtime/mgc0.h"

/*
 * runtime interface and reflection data structures
 */

static  NodeList*       signatlist;
static  Sym*    dtypesym(Type*);
static  Sym*    weaktypesym(Type*);
static  Sym*    dalgsym(Type*);
static  Sym*    dgcsym(Type*);

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 ../../pkg/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;

        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;
        
        // The keys are padded 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;

        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;

        // link up fields
        bucket->type = overflowfield;
        overflowfield->down = keysfield;
        keysfield->down = valuesfield;
        valuesfield->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 ../../pkg/runtime/hashmap.c!
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 += 1;       // keysize
        offset += 1;       // valuesize
        offset = (offset + 1) / 2 * 2;
        offset += 2;       // bucketsize
        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 ../../pkg/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(it->width > widthptr)
                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, 1, 1);
}

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
 * ../../pkg/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
        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
                // ../../pkg/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;
}

enum {
        KindBool = 1,
        KindInt,
        KindInt8,
        KindInt16,
        KindInt32,
        KindInt64,
        KindUint,
        KindUint8,
        KindUint16,
        KindUint32,
        KindUint64,
        KindUintptr,
        KindFloat32,
        KindFloat64,
        KindComplex64,
        KindComplex128,
        KindArray,
        KindChan,
        KindFunc,
        KindInterface,
        KindMap,
        KindPtr,
        KindSlice,
        KindString,
        KindStruct,
        KindUnsafePointer,

        KindNoPointers = 1<<7,
};

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
 * ../../pkg/runtime/type.go:/commonType
 */
static int
dcommontype(Sym *s, int ot, Type *t)
{
        int i, alg, sizeofAlg;
        Sym *sptr, *algsym, *zero;
        static Sym *algarray;
        char *p;
        
        if(ot != 0)
                fatal("dcommontype %d", ot);

        sizeofAlg = 4*widthptr;
        if(algarray == nil)
                algarray = pkglookup("algarray", runtimepkg);
        alg = algtype(t);
        algsym = S;
        if(alg < 0)
                algsym = dalgsym(t);

        dowidth(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

        i = kinds[t->etype];
        if(t->etype == TARRAY && t->bound < 0)
                i = KindSlice;
        if(!haspointers(t))
                i |= KindNoPointers;
        ot = duint8(s, ot, i);  // kind
        if(alg >= 0)
                ot = dsymptr(s, ot, algarray, alg*sizeofAlg);
        else
                ot = dsymptr(s, ot, algsym, 0);
        ot = dsymptr(s, ot, dgcsym(t), 0);  // gc
        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 = tbase->sym == S;

        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) {
                        // ../../pkg/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 {
                        // ../../pkg/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:
                // ../../pkg/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++;
                }

                // ../../pkg/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) {
                        // ../../pkg/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:
                // ../../pkg/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);
                break;

        case TPTR32:
        case TPTR64:
                if(t->type->etype == TANY) {
                        // ../../pkg/runtime/type.go:/UnsafePointerType
                        ot = dcommontype(s, ot, t);
                        break;
                }
                // ../../pkg/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:
                // ../../pkg/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) {
                        // ../../pkg/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, 1);

        // 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, 1);
                }
        }

        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, *eq;
        char buf[100];

        // 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);

        // ../../pkg/runtime/runtime.h:/Alg
        ot = 0;
        ot = dsymptr(s, ot, hash, 0);
        ot = dsymptr(s, ot, eq, 0);
        ot = dsymptr(s, ot, pkglookup("memprint", runtimepkg), 0);
        switch(t->width) {
        default:
                ot = dsymptr(s, ot, pkglookup("memcopy", runtimepkg), 0);
                break;
        case 1:
        case 2:
        case 4:
        case 8:
        case 16:
                snprint(buf, sizeof buf, "memcopy%d", (int)t->width*8);
                ot = dsymptr(s, ot, pkglookup(buf, runtimepkg), 0);
                break;
        }

        ggloblsym(s, ot, 1, 1);
        return s;
}

static int
gcinline(Type *t)
{
        switch(t->etype) {
        case TARRAY:
                if(t->bound == 1)
                        return 1;
                if(t->width <= 4*widthptr)
                        return 1;
                break;
        }
        return 0;
}

static int
dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
{
        Type *t1;
        vlong o, off2, fieldoffset, i;

        if(t->align > 0 && (*off % t->align) != 0)
                fatal("dgcsym1: invalid initial alignment, %T", t);

        if(t->width == BADWIDTH)
                dowidth(t);
        
        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:
                *off += t->width;
                break;

        case TPTR32:
        case TPTR64:
                // NOTE: Any changes here need to be made to reflect.PtrTo as well.
                if(*off % widthptr != 0)
                        fatal("dgcsym1: invalid alignment, %T", t);

                // NOTE(rsc): Emitting GC_APTR here for *nonptrtype
                // (pointer to non-pointer-containing type) means that
                // we do not record 'nonptrtype' and instead tell the 
                // garbage collector to look up the type of the memory in
                // type information stored in the heap. In effect we are telling
                // the collector "we don't trust our information - use yours".
                // It's not completely clear why we want to do this.
                // It does have the effect that if you have a *SliceHeader and a *[]int
                // pointing at the same actual slice header, *SliceHeader will not be
                // used as an authoritative type for the memory, which is good:
                // if the collector scanned the memory as type *SliceHeader, it would
                // see no pointers inside but mark the block as scanned, preventing
                // the seeing of pointers when we followed the *[]int pointer.
                // Perhaps that kind of situation is the rationale.
                if(!haspointers(t->type)) {
                        ot = duintptr(s, ot, GC_APTR);
                        ot = duintptr(s, ot, *off);
                } else {
                        ot = duintptr(s, ot, GC_PTR);
                        ot = duintptr(s, ot, *off);
                        ot = dsymptr(s, ot, dgcsym(t->type), 0);
                }
                *off += t->width;
                break;

        case TUNSAFEPTR:
        case TFUNC:
                if(*off % widthptr != 0)
                        fatal("dgcsym1: invalid alignment, %T", t);
                ot = duintptr(s, ot, GC_APTR);
                ot = duintptr(s, ot, *off);
                *off += t->width;
                break;

        // struct Hchan*
        case TCHAN:
                // NOTE: Any changes here need to be made to reflect.ChanOf as well.
                if(*off % widthptr != 0)
                        fatal("dgcsym1: invalid alignment, %T", t);
                ot = duintptr(s, ot, GC_CHAN_PTR);
                ot = duintptr(s, ot, *off);
                ot = dsymptr(s, ot, dtypesym(t), 0);
                *off += t->width;
                break;

        // struct Hmap*
        case TMAP:
                // NOTE: Any changes here need to be made to reflect.MapOf as well.
                if(*off % widthptr != 0)
                        fatal("dgcsym1: invalid alignment, %T", t);
                ot = duintptr(s, ot, GC_PTR);
                ot = duintptr(s, ot, *off);
                ot = dsymptr(s, ot, dgcsym(hmap(t)), 0);
                *off += t->width;
                break;

        // struct { byte *str; int32 len; }
        case TSTRING:
                if(*off % widthptr != 0)
                        fatal("dgcsym1: invalid alignment, %T", t);
                ot = duintptr(s, ot, GC_STRING);
                ot = duintptr(s, ot, *off);
                *off += t->width;
                break;

        // struct { Itab* tab;  void* data; }
        // struct { Type* type; void* data; }   // When isnilinter(t)==true
        case TINTER:
                if(*off % widthptr != 0)
                        fatal("dgcsym1: invalid alignment, %T", t);
                if(isnilinter(t)) {
                        ot = duintptr(s, ot, GC_EFACE);
                        ot = duintptr(s, ot, *off);
                } else {
                        ot = duintptr(s, ot, GC_IFACE);
                        ot = duintptr(s, ot, *off);
                }
                *off += t->width;
                break;

        case TARRAY:
                if(t->bound < -1)
                        fatal("dgcsym1: invalid bound, %T", t);
                if(t->type->width == BADWIDTH)
                        dowidth(t->type);
                if(isslice(t)) {
                        // NOTE: Any changes here need to be made to reflect.SliceOf as well.
                        // struct { byte* array; uint32 len; uint32 cap; }
                        if(*off % widthptr != 0)
                                fatal("dgcsym1: invalid alignment, %T", t);
                        if(t->type->width != 0) {
                                ot = duintptr(s, ot, GC_SLICE);
                                ot = duintptr(s, ot, *off);
                                ot = dsymptr(s, ot, dgcsym(t->type), 0);
                        } else {
                                ot = duintptr(s, ot, GC_APTR);
                                ot = duintptr(s, ot, *off);
                        }
                        *off += t->width;
                } else {
                        // NOTE: Any changes here need to be made to reflect.ArrayOf as well,
                        // at least once ArrayOf's gc info is implemented and ArrayOf is exported.
                        // struct { byte* array; uint32 len; uint32 cap; }
                        if(t->bound < 1 || !haspointers(t->type)) {
                                *off += t->width;
                        } else if(gcinline(t)) {
                                for(i=0; i<t->bound; i++)
                                        ot = dgcsym1(s, ot, t->type, off, stack_size);  // recursive call of dgcsym1
                        } else {
                                if(stack_size < GC_STACK_CAPACITY) {
                                        ot = duintptr(s, ot, GC_ARRAY_START);  // a stack push during GC
                                        ot = duintptr(s, ot, *off);
                                        ot = duintptr(s, ot, t->bound);
                                        ot = duintptr(s, ot, t->type->width);
                                        off2 = 0;
                                        ot = dgcsym1(s, ot, t->type, &off2, stack_size+1);  // recursive call of dgcsym1
                                        ot = duintptr(s, ot, GC_ARRAY_NEXT);  // a stack pop during GC
                                } else {
                                        ot = duintptr(s, ot, GC_REGION);
                                        ot = duintptr(s, ot, *off);
                                        ot = duintptr(s, ot, t->width);
                                        ot = dsymptr(s, ot, dgcsym(t), 0);
                                }
                                *off += t->width;
                        }
                }
                break;

        case TSTRUCT:
                o = 0;
                for(t1=t->type; t1!=T; t1=t1->down) {
                        fieldoffset = t1->width;
                        *off += fieldoffset - o;
                        ot = dgcsym1(s, ot, t1->type, off, stack_size);  // recursive call of dgcsym1
                        o = fieldoffset + t1->type->width;
                }
                *off += t->width - o;
                break;

        default:
                fatal("dgcsym1: unexpected type %T", t);
        }

        return ot;
}

static Sym*
dgcsym(Type *t)
{
        int ot;
        vlong off;
        Sym *s;

        s = typesymprefix(".gc", t);
        if(s->flags & SymGcgen)
                return s;
        s->flags |= SymGcgen;

        if(t->width == BADWIDTH)
                dowidth(t);

        ot = 0;
        off = 0;
        ot = duintptr(s, ot, t->width);
        ot = dgcsym1(s, ot, t, &off, 0);
        ot = duintptr(s, ot, GC_END);
        ggloblsym(s, ot, 1, 1);

        if(t->align > 0)
                off = rnd(off, t->align);
        if(off != t->width)
                fatal("dgcsym: off=%lld, size=%lld, type %T", off, t->width, t);

        return s;
}

/* [<][>][^][v][top][bottom][index][help] */