// 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 #include #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; ilink) 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; ibound; 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; }