diff --git a/src/cmd/cc/bv.c b/src/cmd/cc/bv.c new file mode 100644 index 0000000000..38d9e4377c --- /dev/null +++ b/src/cmd/cc/bv.c @@ -0,0 +1,46 @@ +// Copyright 2013 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 "cc.h" + +enum { + WORDSIZE = sizeof(uint32), + WORDBITS = 32, +}; + +uintptr +bvsize(uintptr n) +{ + return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE; +} + +Bvec* +bvalloc(int32 n) +{ + Bvec *bv; + uintptr nbytes; + + if(n < 0) + fatal(Z, "bvalloc: initial size is negative\n"); + nbytes = sizeof(Bvec) + bvsize(n); + bv = malloc(nbytes); + if(bv == nil) + fatal(Z, "bvalloc: malloc failed\n"); + memset(bv, 0, nbytes); + bv->n = n; + return bv; +} + +void +bvset(Bvec *bv, int32 i) +{ + uint32 mask; + + if(i < 0 || i >= bv->n) + fatal(Z, "bvset: index %d is out of bounds with length %d\n", i, bv->n); + mask = 1 << (i % WORDBITS); + bv->b[i / WORDBITS] |= mask; +} diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index fe9f9f7985..af2339c976 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -52,6 +52,7 @@ typedef struct Hist Hist; typedef struct Term Term; typedef struct Init Init; typedef struct Bits Bits; +typedef struct Bvec Bvec; typedef struct Dynimp Dynimp; typedef struct Dynexp Dynexp; @@ -76,6 +77,12 @@ struct Bits uint32 b[BITS]; }; +struct Bvec +{ + int32 n; // number of bits + uint32 b[]; +}; + struct Node { Node* left; @@ -750,6 +757,12 @@ Bits blsh(uint); int beq(Bits, Bits); int bset(Bits, uint); +/* + * bv.c + */ +Bvec* bvalloc(int32 n); +void bvset(Bvec *bv, int32 i); + /* * dpchk.c */ diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index 29ab49c42e..4c6859a733 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -31,7 +31,7 @@ #include "gc.h" #include "../../pkg/runtime/funcdata.h" -static int32 pointermap(Sym *gcsym, int32 offset); +static void dumpgcargs(Type *fn, Sym *sym); int hasdotdotdot(void) @@ -82,7 +82,6 @@ codgen(Node *n, Node *nn) Sym *gcsym, *gclocalssym; static int ngcsym, ngclocalssym; static char namebuf[40]; - int32 off; cursafe = 0; curarg = 0; @@ -164,7 +163,9 @@ codgen(Node *n, Node *nn) if(thechar=='6' || thechar=='7') /* [sic] */ maxargsafe = xround(maxargsafe, 8); sp->to.offset += maxargsafe; - + + dumpgcargs(thisfn, gcsym); + // TODO(rsc): "stkoff" is not right. It does not account for // the possibility of data stored in .safe variables. // Unfortunately those move up and down just like @@ -174,16 +175,9 @@ codgen(Node *n, Node *nn) // area its own section. // That said, we've been using stkoff for months // and nothing too terrible has happened. - off = 0; - off = pointermap(gcsym, off); // nptrs and ptrs[...] - gcsym->type = typ(0, T); - gcsym->type->width = off; - - off = 0; - gextern(gclocalssym, nodconst(-stkoff), off, 4); // locals - off += 4; + gextern(gclocalssym, nodconst(-stkoff), 0, 4); // locals gclocalssym->type = typ(0, T); - gclocalssym->type->width = off; + gclocalssym->type->width = 4; } void @@ -652,15 +646,12 @@ bcomplex(Node *n, Node *c) return 0; } -// Makes a bitmap marking the the pointers in t. t starts at the given byte -// offset in the argument list. The returned bitmap should be for pointer -// indexes (relative to offset 0) between baseidx and baseidx+32. -static int32 -pointermap_type(Type *t, int32 offset, int32 baseidx) +// Updates the bitvector with a set bit for each pointer containing +// value in the type description starting at offset. +static void +walktype1(Type *t, int32 offset, Bvec *bv) { Type *t1; - int32 idx; - int32 m; switch(t->etype) { case TCHAR: @@ -676,80 +667,74 @@ pointermap_type(Type *t, int32 offset, int32 baseidx) case TFLOAT: case TDOUBLE: // non-pointer types - return 0; + break; + case TIND: case TARRAY: // unlike Go, C passes arrays by reference // pointer types if((offset + t->offset) % ewidth[TIND] != 0) yyerror("unaligned pointer"); - idx = (offset + t->offset) / ewidth[TIND]; - if(idx >= baseidx && idx < baseidx + 32) - return 1 << (idx - baseidx); - return 0; + bvset(bv, (offset + t->offset) / ewidth[TIND]); + break; + case TSTRUCT: // build map recursively - m = 0; - for(t1=t->link; t1; t1=t1->down) - m |= pointermap_type(t1, offset, baseidx); - return m; + for(t1 = t->link; t1 != T; t1 = t1->down) + walktype1(t1, offset, bv); + break; + case TUNION: - // We require that all elements of the union have the same pointer map. - m = pointermap_type(t->link, offset, baseidx); - for(t1=t->link->down; t1; t1=t1->down) { - if(pointermap_type(t1, offset, baseidx) != m) - yyerror("invalid union in argument list - pointer maps differ"); - } - return m; + walktype1(t->link, offset, bv); + break; + default: yyerror("can't handle arg type %s\n", tnames[t->etype]); - return 0; } } // Compute a bit vector to describe the pointer containing locations // in the argument list. Adds the data to gcsym and returns the offset // of end of the bit vector. -static int32 -pointermap(Sym *gcsym, int32 off) +static void +dumpgcargs(Type *fn, Sym *sym) { - int32 nptrs; - int32 i; - int32 s; // offset in argument list (in bytes) - int32 m; // current ptrs[i/32] + Bvec *bv; Type *t; + int32 i; + int32 symoffset, argoffset; if(hasdotdotdot()) { // give up for C vararg functions. // TODO: maybe make a map just for the args we do know? - gextern(gcsym, nodconst(0), off, 4); // nptrs=0 - return off + 4; - } - nptrs = (argsize() + ewidth[TIND] - 1) / ewidth[TIND]; - gextern(gcsym, nodconst(nptrs), off, 4); - off += 4; - - for(i = 0; i < nptrs; i += 32) { - // generate mask for ptrs at offsets i ... i+31 - m = 0; - s = align(0, thisfn->link, Aarg0, nil); - if(s > 0 && i == 0) { - // C Calling convention returns structs by copying - // them to a location pointed to by a hidden first - // argument. This first argument is a pointer. - if(s != ewidth[TIND]) + gextern(sym, nodconst(0), 0, 4); // nptrs=0 + symoffset = 4; + } else { + bv = bvalloc((argsize() + ewidth[TIND] - 1) / ewidth[TIND]); + argoffset = align(0, fn->link, Aarg0, nil); + if(argoffset > 0) { + // The C calling convention returns structs by + // copying them to a location pointed to by a + // hidden first argument. This first argument + // is a pointer. + if(argoffset != ewidth[TIND]) yyerror("passbyptr arg not the right size"); - m = 1; + bvset(bv, 0); } - for(t=thisfn->down; t!=T; t=t->down) { + for(t = fn->down; t != T; t = t->down) { if(t->etype == TVOID) continue; - s = align(s, t, Aarg1, nil); - m |= pointermap_type(t, s, i); - s = align(s, t, Aarg2, nil); + argoffset = align(argoffset, t, Aarg1, nil); + walktype1(t, argoffset, bv); + argoffset = align(argoffset, t, Aarg2, nil); } - gextern(gcsym, nodconst(m), off, 4); - off += 4; + gextern(sym, nodconst(bv->n), 0, 4); + symoffset = 4; + for(i = 0; i < bv->n; i += 32) { + gextern(sym, nodconst(bv->b[i/32]), symoffset, 4); + symoffset += 4; + } + free(bv); } - return off; - // TODO: needs a test for nptrs>32 + sym->type = typ(0, T); + sym->type->width = symoffset; }