1
0
mirror of https://github.com/golang/go synced 2024-11-23 17:00:07 -07:00

cmd/gc, cmd/ld, runtime: compact liveness bitmaps

Reduce footprint of liveness bitmaps by about 5x.

1. Mark all liveness bitmap symbols as 4-byte aligned
(they were aligned to a larger size by default).

2. The bitmap data is a bitmap count n followed by n bitmaps.
Each bitmap begins with its own count m giving the number
of bits. All the m's are the same for the n bitmaps.
Emit this bitmap length once instead of n times.

3. Many bitmaps within a function have the same bit values,
but each call site was given a distinct bitmap. Merge duplicate
bitmaps so that no bitmap is written more than once.

4. Many functions end up with the same aggregate bitmap data.
We used to name the bitmap data funcname.gcargs and funcname.gclocals.
Instead, name it gclocals.<md5 of data> and mark it dupok so
that the linker coalesces duplicate sets. This cut the bitmap
data remaining after step 3 by 40%; I was not expecting it to
be quite so dramatic.

Applied to "go build -ldflags -w code.google.com/p/go.tools/cmd/godoc":

                bitmaps           pclntab           binary on disk
before this CL  1326600           1985854           12738268
4-byte align    1154288 (0.87x)   1985854 (1.00x)   12566236 (0.99x)
one bitmap len   782528 (0.54x)   1985854 (1.00x)   12193500 (0.96x)
dedup bitmap     414748 (0.31x)   1948478 (0.98x)   11787996 (0.93x)
dedup bitmap set 245580 (0.19x)   1948478 (0.98x)   11620060 (0.91x)

While here, remove various dead blocks of code from plive.c.

Fixes #6929.
Fixes #7568.

LGTM=khr
R=khr
CC=golang-codereviews
https://golang.org/cl/83630044
This commit is contained in:
Russ Cox 2014-04-02 16:49:27 -04:00
parent 9f9c9abb7e
commit 4676fae525
14 changed files with 201 additions and 181 deletions

View File

@ -1508,7 +1508,7 @@ void gused(Node*);
void movelarge(NodeList*); void movelarge(NodeList*);
int isfat(Type*); int isfat(Type*);
void linkarchinit(void); void linkarchinit(void);
void liveness(Node*, Prog*, Sym*, Sym*, Sym*); void liveness(Node*, Prog*, Sym*, Sym*);
void markautoused(Prog*); void markautoused(Prog*);
Plist* newplist(void); Plist* newplist(void);
Node* nodarg(Type*, int); Node* nodarg(Type*, int);

View File

@ -63,7 +63,7 @@ md5write(MD5 *d, uchar *p, int nn)
} }
uint64 uint64
md5sum(MD5 *d) md5sum(MD5 *d, uint64 *hi)
{ {
uchar tmp[64]; uchar tmp[64];
int i; int i;
@ -87,6 +87,8 @@ md5sum(MD5 *d)
if(d->nx != 0) if(d->nx != 0)
fatal("md5sum"); fatal("md5sum");
if(hi != nil)
*hi = d->s[2] | ((uint64)d->s[3]<<32);
return d->s[0] | ((uint64)d->s[1]<<32); return d->s[0] | ((uint64)d->s[1]<<32);
} }

View File

@ -13,4 +13,4 @@ struct MD5
void md5reset(MD5*); void md5reset(MD5*);
void md5write(MD5*, uchar*, int); void md5write(MD5*, uchar*, int);
uint64 md5sum(MD5*); uint64 md5sum(MD5*, uint64*);

View File

@ -8,6 +8,7 @@
#include <u.h> #include <u.h>
#include <libc.h> #include <libc.h>
#include "md5.h"
#include "gg.h" #include "gg.h"
#include "opt.h" #include "opt.h"
#include "../../pkg/runtime/funcdata.h" #include "../../pkg/runtime/funcdata.h"
@ -130,6 +131,23 @@ removevardef(Prog *firstp)
} }
} }
static void
gcsymdup(Sym *s)
{
LSym *ls;
uint64 lo, hi;
ls = linksym(s);
if(ls->nr > 0)
fatal("cannot rosymdup %s with relocations", ls->name);
MD5 d;
md5reset(&d);
md5write(&d, ls->p, ls->np);
lo = md5sum(&d, &hi);
ls->name = smprint("gclocals·%016llux%016llux", lo, hi);
ls->dupok = 1;
}
void void
compile(Node *fn) compile(Node *fn)
{ {
@ -143,7 +161,6 @@ compile(Node *fn)
NodeList *l; NodeList *l;
Sym *gcargs; Sym *gcargs;
Sym *gclocals; Sym *gclocals;
Sym *gcdead;
if(newproc == N) { if(newproc == N) {
newproc = sysfunc("newproc"); newproc = sysfunc("newproc");
@ -227,15 +244,6 @@ compile(Node *fn)
gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps); gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps);
gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps); gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps);
// TODO(cshapiro): emit the dead value map when the garbage collector
// pre-verification pass is checked in. It is otherwise harmless to
// emit this information if it is not used but it does cost RSS at
// compile time. At present, the amount of additional RSS is
// substantial enough to affect our smallest build machines.
if(0)
gcdead = makefuncdatasym("gcdead·%d", FUNCDATA_DeadValueMaps);
else
gcdead = nil;
for(t=curfn->paramfld; t; t=t->down) for(t=curfn->paramfld; t; t=t->down)
gtrack(tracksym(t->type)); gtrack(tracksym(t->type));
@ -304,7 +312,9 @@ compile(Node *fn)
} }
// Emit garbage collection symbols. // Emit garbage collection symbols.
liveness(curfn, ptxt, gcargs, gclocals, gcdead); liveness(curfn, ptxt, gcargs, gclocals);
gcsymdup(gcargs);
gcsymdup(gclocals);
defframe(ptxt); defframe(ptxt);

View File

@ -119,11 +119,6 @@ struct Liveness {
// in the arguments and locals area, indexed by bb->rpo. // in the arguments and locals area, indexed by bb->rpo.
Array *argslivepointers; Array *argslivepointers;
Array *livepointers; Array *livepointers;
// An array with a bit vector for each safe point tracking dead values
// pointers in the arguments and locals area, indexed by bb->rpo.
Array *argsdeadvalues;
Array *deadvalues;
}; };
static void* static void*
@ -270,7 +265,7 @@ blockany(BasicBlock *bb, int (*callback)(Prog*))
// Collects and returns and array of Node*s for functions arguments and local // Collects and returns and array of Node*s for functions arguments and local
// variables. // variables.
static Array* static Array*
getvariables(Node *fn, int allvalues) getvariables(Node *fn)
{ {
Array *result; Array *result;
NodeList *ll; NodeList *ll;
@ -282,7 +277,7 @@ getvariables(Node *fn, int allvalues)
case PAUTO: case PAUTO:
case PPARAM: case PPARAM:
case PPARAMOUT: case PPARAMOUT:
if(haspointers(ll->n->type) || allvalues) if(haspointers(ll->n->type))
arrayadd(result, &ll->n); arrayadd(result, &ll->n);
break; break;
} }
@ -764,7 +759,7 @@ Next1:;
// liveness computation. The cfg argument is an array of BasicBlock*s and the // liveness computation. The cfg argument is an array of BasicBlock*s and the
// vars argument is an array of Node*s. // vars argument is an array of Node*s.
static Liveness* static Liveness*
newliveness(Node *fn, Prog *ptxt, Array *cfg, Array *vars, int computedead) newliveness(Node *fn, Prog *ptxt, Array *cfg, Array *vars)
{ {
Liveness *result; Liveness *result;
int32 i; int32 i;
@ -799,13 +794,6 @@ newliveness(Node *fn, Prog *ptxt, Array *cfg, Array *vars, int computedead)
result->livepointers = arraynew(0, sizeof(Bvec*)); result->livepointers = arraynew(0, sizeof(Bvec*));
result->argslivepointers = arraynew(0, sizeof(Bvec*)); result->argslivepointers = arraynew(0, sizeof(Bvec*));
if(computedead) {
result->deadvalues = arraynew(0, sizeof(Bvec*));
result->argsdeadvalues = arraynew(0, sizeof(Bvec*));
} else {
result->deadvalues = nil;
result->argsdeadvalues = nil;
}
return result; return result;
} }
@ -826,16 +814,6 @@ freeliveness(Liveness *lv)
free(*(Bvec**)arrayget(lv->argslivepointers, i)); free(*(Bvec**)arrayget(lv->argslivepointers, i));
arrayfree(lv->argslivepointers); arrayfree(lv->argslivepointers);
if(lv->deadvalues != nil) {
for(i = 0; i < arraylength(lv->deadvalues); i++)
free(*(Bvec**)arrayget(lv->deadvalues, i));
arrayfree(lv->deadvalues);
for(i = 0; i < arraylength(lv->argsdeadvalues); i++)
free(*(Bvec**)arrayget(lv->argsdeadvalues, i));
arrayfree(lv->argsdeadvalues);
}
for(i = 0; i < arraylength(lv->cfg); i++) { for(i = 0; i < arraylength(lv->cfg); i++) {
free(lv->uevar[i]); free(lv->uevar[i]);
free(lv->varkill[i]); free(lv->varkill[i]);
@ -1218,54 +1196,6 @@ twobitlivepointermap(Liveness *lv, Bvec *liveout, Array *vars, Bvec *args, Bvec
} }
} }
// Generates dead value maps for arguments and local variables. Dead values of
// any type are tracked, not just pointers. The this argument and the in
// arguments are never assumed dead. The vars argument is an array of Node*s.
static void
twobitdeadvaluemap(Liveness *lv, Bvec *liveout, Array *vars, Bvec *args, Bvec *locals)
{
Node *node;
/*
Type *thisargtype;
Type *inargtype;
*/
vlong xoffset;
int32 i;
for(i = 0; i < arraylength(vars); i++) {
node = *(Node**)arrayget(vars, i);
switch(node->class) {
case PAUTO:
if(!bvget(liveout, i)) {
xoffset = node->xoffset + stkptrsize;
twobitwalktype1(node->type, &xoffset, locals);
}
break;
case PPARAM:
case PPARAMOUT:
if(!bvget(liveout, i)) {
xoffset = node->xoffset;
twobitwalktype1(node->type, &xoffset, args);
}
break;
}
}
USED(lv);
/*
thisargtype = getinargx(lv->fn->type);
if(thisargtype != nil) {
xoffset = 0;
twobitwalktype1(thisargtype, &xoffset, args);
}
inargtype = getinargx(lv->fn->type);
if(inargtype != nil) {
xoffset = 0;
twobitwalktype1(inargtype, &xoffset, args);
}
*/
}
// Construct a disembodied instruction. // Construct a disembodied instruction.
static Prog* static Prog*
unlinkedprog(int as) unlinkedprog(int as)
@ -1574,14 +1504,6 @@ livenessepilogue(Liveness *lv)
// over the block (as this loop does), while the liveout // over the block (as this loop does), while the liveout
// requires walking backward (as the next loop does). // requires walking backward (as the next loop does).
twobitlivepointermap(lv, any, lv->vars, args, locals); twobitlivepointermap(lv, any, lv->vars, args, locals);
// Dead stuff second.
if(lv->deadvalues != nil) {
args = bvalloc(argswords() * BitsPerPointer);
arrayadd(lv->argsdeadvalues, &args);
locals = bvalloc(localswords() * BitsPerPointer);
arrayadd(lv->deadvalues, &locals);
}
} }
if(p == bb->last) if(p == bb->last)
@ -1669,13 +1591,6 @@ livenessepilogue(Liveness *lv)
msg[--startmsg] = fmtstrflush(&fmt); msg[--startmsg] = fmtstrflush(&fmt);
} }
// Record dead values.
if(lv->deadvalues != nil) {
args = *(Bvec**)arrayget(lv->argsdeadvalues, pos);
locals = *(Bvec**)arrayget(lv->deadvalues, pos);
twobitdeadvaluemap(lv, liveout, lv->vars, args, locals);
}
// Only CALL instructions need a PCDATA annotation. // Only CALL instructions need a PCDATA annotation.
// The TEXT instruction annotation is implicit. // The TEXT instruction annotation is implicit.
if(p->as == ACALL) { if(p->as == ACALL) {
@ -1717,6 +1632,113 @@ livenessepilogue(Liveness *lv)
flusherrors(); flusherrors();
} }
// FNV-1 hash function constants.
#define H0 2166136261
#define Hp 16777619
static uint32
hashbitmap(uint32 h, Bvec *bv)
{
uchar *p, *ep;
p = (uchar*)bv->b;
ep = p + 4*((bv->n+31)/32);
while(p < ep)
h = (h*Hp) ^ *p++;
return h;
}
// Compact liveness information by coalescing identical per-call-site bitmaps.
// The merging only happens for a single function, not across the entire binary.
//
// There are actually two lists of bitmaps, one list for the local variables and one
// list for the function arguments. Both lists are indexed by the same PCDATA
// index, so the corresponding pairs must be considered together when
// merging duplicates. The argument bitmaps change much less often during
// function execution than the local variable bitmaps, so it is possible that
// we could introduce a separate PCDATA index for arguments vs locals and
// then compact the set of argument bitmaps separately from the set of
// local variable bitmaps. As of 2014-04-02, doing this to the godoc binary
// is actually a net loss: we save about 50k of argument bitmaps but the new
// PCDATA tables cost about 100k. So for now we keep using a single index for
// both bitmap lists.
static void
livenesscompact(Liveness *lv)
{
int *table, *remap, i, j, n, tablesize, uniq;
uint32 h;
Bvec *local, *arg, *jlocal, *jarg;
Prog *p;
// Linear probing hash table of bitmaps seen so far.
// The hash table has 4n entries to keep the linear
// scan short. An entry of -1 indicates an empty slot.
n = arraylength(lv->livepointers);
tablesize = 4*n;
table = xmalloc(tablesize*sizeof table[0]);
memset(table, 0xff, tablesize*sizeof table[0]);
// remap[i] = the new index of the old bit vector #i.
remap = xmalloc(n*sizeof remap[0]);
memset(remap, 0xff, n*sizeof remap[0]);
uniq = 0; // unique tables found so far
// Consider bit vectors in turn.
// If new, assign next number using uniq,
// record in remap, record in lv->livepointers and lv->argslivepointers
// under the new index, and add entry to hash table.
// If already seen, record earlier index in remap and free bitmaps.
for(i=0; i<n; i++) {
local = *(Bvec**)arrayget(lv->livepointers, i);
arg = *(Bvec**)arrayget(lv->argslivepointers, i);
h = hashbitmap(hashbitmap(H0, local), arg) % tablesize;
for(;;) {
j = table[h];
if(j < 0)
break;
jlocal = *(Bvec**)arrayget(lv->livepointers, j);
jarg = *(Bvec**)arrayget(lv->argslivepointers, j);
if(bvcmp(local, jlocal) == 0 && bvcmp(arg, jarg) == 0) {
free(local);
free(arg);
remap[i] = j;
goto Next;
}
if(++h == tablesize)
h = 0;
}
table[h] = uniq;
remap[i] = uniq;
*(Bvec**)arrayget(lv->livepointers, uniq) = local;
*(Bvec**)arrayget(lv->argslivepointers, uniq) = arg;
uniq++;
Next:;
}
// We've already reordered lv->livepointers[0:uniq]
// and lv->argslivepointers[0:uniq] and freed the bitmaps
// we don't need anymore. Clear the pointers later in the
// array so that we can tell where the coalesced bitmaps stop
// and so that we don't double-free when cleaning up.
for(j=uniq; j<n; j++) {
*(Bvec**)arrayget(lv->livepointers, j) = nil;
*(Bvec**)arrayget(lv->argslivepointers, j) = nil;
}
// Rewrite PCDATA instructions to use new numbering.
for(p=lv->ptxt; p != P; p=p->link) {
if(p->as == APCDATA && p->from.offset == PCDATA_StackMapIndex) {
i = p->to.offset;
if(i >= 0)
p->to.offset = remap[i];
}
}
free(table);
free(remap);
}
static int static int
printbitset(int printed, char *name, Array *vars, Bvec *bits) printbitset(int printed, char *name, Array *vars, Bvec *bits)
{ {
@ -1843,44 +1865,28 @@ livenessprintdebug(Liveness *lv)
// words that are followed are the raw bitmap words. The arr argument is an // words that are followed are the raw bitmap words. The arr argument is an
// array of Node*s. // array of Node*s.
static void static void
twobitwritesymbol(Array *arr, Sym *sym, Bvec *check) twobitwritesymbol(Array *arr, Sym *sym)
{ {
Bvec *bv; Bvec *bv;
int off, i, j, len, pos; int off, i, j, len;
uint32 bit, word, checkword; uint32 word;
len = arraylength(arr); len = arraylength(arr);
// Dump the length of the bitmap array. off = 0;
off = duint32(sym, 0, len); off += 4; // number of bitmaps, to fill in later
bv = *(Bvec**)arrayget(arr, 0);
off = duint32(sym, off, bv->n); // number of bits in each bitmap
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
// bitmap words
bv = *(Bvec**)arrayget(arr, i); bv = *(Bvec**)arrayget(arr, i);
// If we have been provided a check bitmap we can use it if(bv == nil)
// to confirm that the bitmap we are dumping is a subset break;
// of the check bitmap.
if(check != nil) {
for(j = 0; j < bv->n; j += 32) {
word = bv->b[j/32];
checkword = check->b[j/32];
if(word != checkword) {
// Found a mismatched word; find the mismatched bit.
for(pos = 0; pos < 32; pos++) {
bit = 1 << pos;
if((word & bit) && !(checkword & bit)) {
print("twobitwritesymbol: expected %032b to be a subset of %032b\n", word, checkword);
fatal("mismatch at bit position %d\n", pos);
}
}
}
}
}
// Dump the length of the bitmap.
off = duint32(sym, off, bv->n);
// Dump the words of the bitmap.
for(j = 0; j < bv->n; j += 32) { for(j = 0; j < bv->n; j += 32) {
word = bv->b[j/32]; word = bv->b[j/32];
off = duint32(sym, off, word); off = duint32(sym, off, word);
} }
} }
duint32(sym, 0, i); // number of bitmaps
ggloblsym(sym, off, 0, 1); ggloblsym(sym, off, 0, 1);
} }
@ -1897,7 +1903,7 @@ printprog(Prog *p)
// the liveness of pointer variables in the function, and emits a runtime data // the liveness of pointer variables in the function, and emits a runtime data
// structure read by the garbage collector. // structure read by the garbage collector.
void void
liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym, Sym *deadsym) liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
{ {
Array *cfg, *vars; Array *cfg, *vars;
Liveness *lv; Liveness *lv;
@ -1919,8 +1925,8 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym, Sym *deadsym)
cfg = newcfg(firstp); cfg = newcfg(firstp);
if(debuglive >= 3) if(debuglive >= 3)
printcfg(cfg); printcfg(cfg);
vars = getvariables(fn, deadsym != nil); vars = getvariables(fn);
lv = newliveness(fn, firstp, cfg, vars, deadsym != nil); lv = newliveness(fn, firstp, cfg, vars);
// Run the dataflow framework. // Run the dataflow framework.
livenessprologue(lv); livenessprologue(lv);
@ -1930,17 +1936,16 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym, Sym *deadsym)
if(debuglive >= 3) if(debuglive >= 3)
livenessprintcfg(lv); livenessprintcfg(lv);
livenessepilogue(lv); livenessepilogue(lv);
if(debuglive >= 3)
livenessprintcfg(lv);
livenesscompact(lv);
if(debuglive >= 2) if(debuglive >= 2)
livenessprintdebug(lv); livenessprintdebug(lv);
// Emit the live pointer map data structures // Emit the live pointer map data structures
twobitwritesymbol(lv->livepointers, livesym, nil); twobitwritesymbol(lv->livepointers, livesym);
twobitwritesymbol(lv->argslivepointers, argssym, nil); twobitwritesymbol(lv->argslivepointers, argssym);
// Optionally emit a dead value map data structure for locals.
if(deadsym != nil)
twobitwritesymbol(lv->deadvalues, deadsym, nil);
// Free everything. // Free everything.
freeliveness(lv); freeliveness(lv);

View File

@ -1678,7 +1678,7 @@ typehash(Type *t)
md5reset(&d); md5reset(&d);
md5write(&d, (uchar*)p, strlen(p)); md5write(&d, (uchar*)p, strlen(p));
free(p); free(p);
return md5sum(&d); return md5sum(&d, nil);
} }
Type* Type*

View File

@ -98,6 +98,7 @@ EXTERN char* paramspace;
EXTERN int nerrors; EXTERN int nerrors;
EXTERN int linkmode; EXTERN int linkmode;
EXTERN int64 liveness;
// for dynexport field of LSym // for dynexport field of LSym
enum enum

View File

@ -189,6 +189,7 @@ main(int argc, char *argv[])
Bprint(&bso, "%d symbols\n", ctxt->nsymbol); Bprint(&bso, "%d symbols\n", ctxt->nsymbol);
Bprint(&bso, "%d sizeof adr\n", sizeof(Addr)); Bprint(&bso, "%d sizeof adr\n", sizeof(Addr));
Bprint(&bso, "%d sizeof prog\n", sizeof(Prog)); Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
Bprint(&bso, "%lld liveness data\n", liveness);
} }
Bflush(&bso); Bflush(&bso);

View File

@ -429,6 +429,8 @@ symtab(void)
s->type = SGOFUNC; s->type = SGOFUNC;
s->hide = 1; s->hide = 1;
s->outer = symgofunc; s->outer = symgofunc;
s->align = 4;
liveness += (s->size+s->align-1)&~(s->align-1);
} }
} }
} }

View File

@ -485,7 +485,7 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn)
static int ndup; static int ndup;
char *name; char *name;
Reloc *r; Reloc *r;
LSym *s; LSym *s, *dup;
Pcln *pc; Pcln *pc;
Auto *a; Auto *a;
@ -502,11 +502,14 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn)
if(v != 0) if(v != 0)
v = ctxt->version; v = ctxt->version;
s = linklookup(ctxt, name, v); s = linklookup(ctxt, name, v);
dup = nil;
if(s->type != 0 && s->type != SXREF) { if(s->type != 0 && s->type != SXREF) {
if(s->type != SBSS && s->type != SNOPTRBSS && !dupok && !s->dupok) if(s->type != SBSS && s->type != SNOPTRBSS && !dupok && !s->dupok)
sysfatal("duplicate symbol %s (types %d and %d) in %s and %s", s->name, s->type, t, s->file, pn); sysfatal("duplicate symbol %s (types %d and %d) in %s and %s", s->name, s->type, t, s->file, pn);
if(s->np > 0) if(s->np > 0) {
dup = s;
s = linklookup(ctxt, ".dup", ndup++); // scratch s = linklookup(ctxt, ".dup", ndup++); // scratch
}
} }
s->file = pkg; s->file = pkg;
s->dupok = dupok; s->dupok = dupok;
@ -537,6 +540,13 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn)
} }
} }
if(s->np > 0 && dup != nil && dup->np > 0 && strncmp(s->name, "gclocals·", 10) == 0) {
// content-addressed garbage collection liveness bitmap symbol.
// double check for hash collisions.
if(s->np != dup->np || memcmp(s->p, dup->p, s->np) != 0)
sysfatal("dupok hash collision for %s in %s and %s", s->name, s->file, pn);
}
if(s->type == STEXT) { if(s->type == STEXT) {
s->args = rdint(f); s->args = rdint(f);
s->locals = rdint(f); s->locals = rdint(f);

View File

@ -244,7 +244,7 @@ struct ChildInfo {
// the layout of the outargs region. // the layout of the outargs region.
uintptr argoff; // where the arguments start in the frame uintptr argoff; // where the arguments start in the frame
uintptr arglen; // size of args region uintptr arglen; // size of args region
BitVector *args; // if not nil, pointer map of args region BitVector args; // if args.n >= 0, pointer map of args region
byte *sp; // callee sp byte *sp; // callee sp
uintptr depth; // depth in call stack (0 == most recent) uintptr depth; // depth in call stack (0 == most recent)
@ -301,7 +301,7 @@ dumpframe(Stkframe *s, void *arg)
int32 pcdata; int32 pcdata;
StackMap *stackmap; StackMap *stackmap;
int8 *name; int8 *name;
BitVector *bv; BitVector bv;
child = (ChildInfo*)arg; child = (ChildInfo*)arg;
f = s->fn; f = s->fn;
@ -320,13 +320,13 @@ dumpframe(Stkframe *s, void *arg)
stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps); stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
// Dump any types we will need to resolve Efaces. // Dump any types we will need to resolve Efaces.
if(child->args != nil) if(child->args.n >= 0)
dumpbvtypes(child->args, (byte*)s->sp + child->argoff); dumpbvtypes(&child->args, (byte*)s->sp + child->argoff);
if(stackmap != nil && stackmap->n > 0) { if(stackmap != nil && stackmap->n > 0) {
bv = runtime·stackmapdata(stackmap, pcdata); bv = runtime·stackmapdata(stackmap, pcdata);
dumpbvtypes(bv, s->varp - bv->n / BitsPerPointer * PtrSize); dumpbvtypes(&bv, s->varp - bv.n / BitsPerPointer * PtrSize);
} else { } else {
bv = nil; bv.n = -1;
} }
// Dump main body of stack frame. // Dump main body of stack frame.
@ -343,8 +343,8 @@ dumpframe(Stkframe *s, void *arg)
dumpcstr(name); dumpcstr(name);
// Dump fields in the outargs section // Dump fields in the outargs section
if(child->args != nil) { if(child->args.n >= 0) {
dumpbv(child->args, child->argoff); dumpbv(&child->args, child->argoff);
} else { } else {
// conservative - everything might be a pointer // conservative - everything might be a pointer
for(off = child->argoff; off < child->argoff + child->arglen; off += PtrSize) { for(off = child->argoff; off < child->argoff + child->arglen; off += PtrSize) {
@ -370,7 +370,7 @@ dumpframe(Stkframe *s, void *arg)
} else if(stackmap->n > 0) { } else if(stackmap->n > 0) {
// Locals bitmap information, scan just the pointers in // Locals bitmap information, scan just the pointers in
// locals. // locals.
dumpbv(bv, s->varp - bv->n / BitsPerPointer * PtrSize - (byte*)s->sp); dumpbv(&bv, s->varp - bv.n / BitsPerPointer * PtrSize - (byte*)s->sp);
} }
dumpint(FieldKindEol); dumpint(FieldKindEol);
@ -383,7 +383,7 @@ dumpframe(Stkframe *s, void *arg)
if(stackmap != nil) if(stackmap != nil)
child->args = runtime·stackmapdata(stackmap, pcdata); child->args = runtime·stackmapdata(stackmap, pcdata);
else else
child->args = nil; child->args.n = -1;
return true; return true;
} }
@ -421,7 +421,7 @@ dumpgoroutine(G *gp)
dumpint((uintptr)gp->panic); dumpint((uintptr)gp->panic);
// dump stack // dump stack
child.args = nil; child.args.n = -1;
child.arglen = 0; child.arglen = 0;
child.sp = nil; child.sp = nil;
child.depth = 0; child.depth = 0;

View File

@ -602,12 +602,13 @@ typedef struct BitVector BitVector;
struct BitVector struct BitVector
{ {
int32 n; // # of bits int32 n; // # of bits
uint32 data[]; uint32 *data;
}; };
typedef struct StackMap StackMap; typedef struct StackMap StackMap;
struct StackMap struct StackMap
{ {
int32 n; int32 n; // number of bitmaps
int32 nbit; // number of bits in each bitmap
uint32 data[]; uint32 data[];
}; };
enum { enum {
@ -626,7 +627,7 @@ enum {
}; };
// Returns pointer map data for the given stackmap index // Returns pointer map data for the given stackmap index
// (the index is encoded in PCDATA_StackMapIndex). // (the index is encoded in PCDATA_StackMapIndex).
BitVector* runtime·stackmapdata(StackMap *stackmap, int32 n); BitVector runtime·stackmapdata(StackMap *stackmap, int32 n);
// defined in mgc0.go // defined in mgc0.go
void runtime·gc_m_ptr(Eface*); void runtime·gc_m_ptr(Eface*);
@ -636,4 +637,4 @@ void runtime·memorydump(void);
int32 runtime·setgcpercent(int32); int32 runtime·setgcpercent(int32);
// Value we use to mark dead pointers when GODEBUG=gcdead=1. // Value we use to mark dead pointers when GODEBUG=gcdead=1.
#define PoisonPtr ((uintptr)0x6969696969696969LL) #define PoisonPtr ((uintptr)0xf9696969f9696969LL)

View File

@ -1403,24 +1403,12 @@ handoff(Workbuf *b)
extern byte pclntab[]; // base for f->ptrsoff extern byte pclntab[]; // base for f->ptrsoff
BitVector* BitVector
runtime·stackmapdata(StackMap *stackmap, int32 n) runtime·stackmapdata(StackMap *stackmap, int32 n)
{ {
BitVector *bv; if(n < 0 || n >= stackmap->n)
uint32 *ptr;
uint32 words;
int32 i;
if(n < 0 || n >= stackmap->n) {
runtime·throw("stackmapdata: index out of range"); runtime·throw("stackmapdata: index out of range");
} return (BitVector){stackmap->nbit, stackmap->data + n*((stackmap->nbit+31)/32)};
ptr = stackmap->data;
for(i = 0; i < n; i++) {
bv = (BitVector*)ptr;
words = ((bv->n + 31) / 32) + 1;
ptr += words;
}
return (BitVector*)ptr;
} }
// Scans an interface data value when the interface type indicates // Scans an interface data value when the interface type indicates
@ -1533,7 +1521,7 @@ scanframe(Stkframe *frame, void *wbufp)
{ {
Func *f; Func *f;
StackMap *stackmap; StackMap *stackmap;
BitVector *bv; BitVector bv;
uintptr size; uintptr size;
uintptr targetpc; uintptr targetpc;
int32 pcdata; int32 pcdata;
@ -1576,9 +1564,9 @@ scanframe(Stkframe *frame, void *wbufp)
runtime·throw("scanframe: bad symbol table"); runtime·throw("scanframe: bad symbol table");
} }
bv = runtime·stackmapdata(stackmap, pcdata); bv = runtime·stackmapdata(stackmap, pcdata);
size = (bv->n * PtrSize) / BitsPerPointer; size = (bv.n * PtrSize) / BitsPerPointer;
precise = true; precise = true;
scanbitvector(f, true, frame->varp - size, bv, afterprologue, wbufp); scanbitvector(f, true, frame->varp - size, &bv, afterprologue, wbufp);
} }
} }
@ -1587,7 +1575,7 @@ scanframe(Stkframe *frame, void *wbufp)
stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps); stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
if(stackmap != nil) { if(stackmap != nil) {
bv = runtime·stackmapdata(stackmap, pcdata); bv = runtime·stackmapdata(stackmap, pcdata);
scanbitvector(f, precise, frame->argp, bv, true, wbufp); scanbitvector(f, precise, frame->argp, &bv, true, wbufp);
} else } else
enqueue1(wbufp, (Obj){frame->argp, frame->arglen, 0}); enqueue1(wbufp, (Obj){frame->argp, frame->arglen, 0});
return true; return true;

View File

@ -436,7 +436,7 @@ adjustframe(Stkframe *frame, void *arg)
Func *f; Func *f;
StackMap *stackmap; StackMap *stackmap;
int32 pcdata; int32 pcdata;
BitVector *bv; BitVector bv;
uintptr targetpc; uintptr targetpc;
adjinfo = arg; adjinfo = arg;
@ -462,7 +462,7 @@ adjustframe(Stkframe *frame, void *arg)
bv = runtime·stackmapdata(stackmap, pcdata); bv = runtime·stackmapdata(stackmap, pcdata);
if(StackDebug >= 3) if(StackDebug >= 3)
runtime·printf(" locals\n"); runtime·printf(" locals\n");
adjustpointers((byte**)frame->varp - bv->n / BitsPerPointer, bv, adjinfo, f); adjustpointers((byte**)frame->varp - bv.n / BitsPerPointer, &bv, adjinfo, f);
} }
// adjust inargs and outargs // adjust inargs and outargs
if(frame->arglen != 0) { if(frame->arglen != 0) {
@ -472,7 +472,7 @@ adjustframe(Stkframe *frame, void *arg)
bv = runtime·stackmapdata(stackmap, pcdata); bv = runtime·stackmapdata(stackmap, pcdata);
if(StackDebug >= 3) if(StackDebug >= 3)
runtime·printf(" args\n"); runtime·printf(" args\n");
adjustpointers((byte**)frame->argp, bv, adjinfo, nil); adjustpointers((byte**)frame->argp, &bv, adjinfo, nil);
} }
return true; return true;
} }
@ -491,7 +491,7 @@ adjustdefers(G *gp, AdjustInfo *adjinfo)
Func *f; Func *f;
FuncVal *fn; FuncVal *fn;
StackMap *stackmap; StackMap *stackmap;
BitVector *bv; BitVector bv;
for(dp = &gp->defer, d = *dp; d != nil; dp = &d->link, d = *dp) { for(dp = &gp->defer, d = *dp; d != nil; dp = &d->link, d = *dp) {
if(adjinfo->oldstk <= (byte*)d && (byte*)d < adjinfo->oldbase) { if(adjinfo->oldstk <= (byte*)d && (byte*)d < adjinfo->oldbase) {
@ -526,7 +526,7 @@ adjustdefers(G *gp, AdjustInfo *adjinfo)
if(stackmap == nil) if(stackmap == nil)
runtime·throw("runtime: deferred function has no arg ptr map"); runtime·throw("runtime: deferred function has no arg ptr map");
bv = runtime·stackmapdata(stackmap, 0); bv = runtime·stackmapdata(stackmap, 0);
adjustpointers(d->args, bv, adjinfo, f); adjustpointers(d->args, &bv, adjinfo, f);
} }
d->argp += adjinfo->delta; d->argp += adjinfo->delta;
} }