Commit 4676fae5 authored by Russ Cox's avatar Russ Cox

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
parent 9f9c9abb
...@@ -1508,7 +1508,7 @@ void gused(Node*); ...@@ -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);
......
...@@ -63,7 +63,7 @@ md5write(MD5 *d, uchar *p, int nn) ...@@ -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) ...@@ -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);
} }
......
...@@ -13,4 +13,4 @@ struct MD5 ...@@ -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*);
...@@ -8,6 +8,7 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
......
This diff is collapsed.
...@@ -1678,7 +1678,7 @@ typehash(Type *t) ...@@ -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*
......
...@@ -98,6 +98,7 @@ EXTERN char* paramspace; ...@@ -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
......
...@@ -189,6 +189,7 @@ main(int argc, char *argv[]) ...@@ -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);
......
...@@ -429,6 +429,8 @@ symtab(void) ...@@ -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);
} }
} }
} }
...@@ -485,7 +485,7 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) ...@@ -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,12 +502,15 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) ...@@ -502,12 +502,15 @@ 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;
if(t == SXREF) if(t == SXREF)
...@@ -537,6 +540,13 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) ...@@ -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);
......
...@@ -244,7 +244,7 @@ struct ChildInfo { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -602,12 +602,13 @@ typedef struct BitVector BitVector; ...@@ -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 { ...@@ -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); ...@@ -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)
...@@ -1403,24 +1403,12 @@ handoff(Workbuf *b) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -436,7 +436,7 @@ adjustframe(Stkframe *frame, void *arg) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment