aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/ld/data.c
diff options
context:
space:
mode:
authorDmitriy Vyukov <dvyukov@google.com>2014-07-29 11:01:02 +0400
committerDmitriy Vyukov <dvyukov@google.com>2014-07-29 11:01:02 +0400
commitcd17a717f9f49cffb40fbdef59d1dfac1f9e0cfd (patch)
tree9ecec51ca71307c73ac6cdf6522855e0e8b594d8 /src/cmd/ld/data.c
parent0100afbdcc065ec20631d60cf7621d642f44b9d5 (diff)
downloadgo-cd17a717f9f49cffb40fbdef59d1dfac1f9e0cfd.tar.xz
runtime: simpler and faster GC
Implement the design described in: https://docs.google.com/document/d/1v4Oqa0WwHunqlb8C3ObL_uNQw3DfSY-ztoA-4wWbKcg/pub Summary of the changes: GC uses "2-bits per word" pointer type info embed directly into bitmap. Scanning of stacks/data/heap is unified. The old spans types go away. Compiler generates "sparse" 4-bits type info for GC (directly for GC bitmap). Linker generates "dense" 2-bits type info for data/bss (the same as stacks use). Summary of results: -1680 lines of code total (-1000+ in mgc0.c only) -25% memory consumption -3-7% binary size -15% GC pause reduction -7% run time reduction LGTM=khr R=golang-codereviews, rsc, christoph, khr CC=golang-codereviews, rlh https://golang.org/cl/106260045
Diffstat (limited to 'src/cmd/ld/data.c')
-rw-r--r--src/cmd/ld/data.c196
1 files changed, 162 insertions, 34 deletions
diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c
index b2075f2d66..e8e697f15e 100644
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -706,31 +706,165 @@ maxalign(LSym *s, int type)
return max;
}
+// Helper object for building GC type programs.
+typedef struct ProgGen ProgGen;
+struct ProgGen
+{
+ LSym* s;
+ int32 datasize;
+ uint8 data[256/PointersPerByte];
+ vlong pos;
+};
+
static void
-gcaddsym(LSym *gc, LSym *s, vlong off)
+proggeninit(ProgGen *g, LSym *s)
{
- vlong a;
- LSym *gotype;
+ g->s = s;
+ g->datasize = 0;
+ g->pos = 0;
+ memset(g->data, 0, sizeof(g->data));
+}
+
+static void
+proggenemit(ProgGen *g, uint8 v)
+{
+ adduint8(ctxt, g->s, v);
+}
- if(s->size < PtrSize)
+// Writes insData block from g->data.
+static void
+proggendataflush(ProgGen *g)
+{
+ int32 i, s;
+
+ if(g->datasize == 0)
return;
- if(strcmp(s->name, ".string") == 0)
+ proggenemit(g, insData);
+ proggenemit(g, g->datasize);
+ s = (g->datasize + PointersPerByte - 1)/PointersPerByte;
+ for(i = 0; i < s; i++)
+ proggenemit(g, g->data[i]);
+ g->datasize = 0;
+ memset(g->data, 0, sizeof(g->data));
+}
+
+static void
+proggendata(ProgGen *g, uint8 d)
+{
+ g->data[g->datasize/PointersPerByte] |= d << ((g->datasize%PointersPerByte)*BitsPerPointer);
+ g->datasize++;
+ if(g->datasize == 255)
+ proggendataflush(g);
+}
+
+// Skip v bytes due to alignment, etc.
+static void
+proggenskip(ProgGen *g, vlong off, vlong v)
+{
+ vlong i;
+
+ for(i = off; i < off+v; i++) {
+ if((i%PtrSize) == 0)
+ proggendata(g, BitsScalar);
+ }
+}
+
+// Emit insArray instruction.
+static void
+proggenarray(ProgGen *g, vlong len)
+{
+ int32 i;
+
+ proggendataflush(g);
+ proggenemit(g, insArray);
+ for(i = 0; i < PtrSize; i++, len >>= 8)
+ proggenemit(g, len);
+}
+
+static void
+proggenarrayend(ProgGen *g)
+{
+ proggendataflush(g);
+ proggenemit(g, insArrayEnd);
+}
+
+static void
+proggenfini(ProgGen *g, vlong size)
+{
+ proggenskip(g, g->pos, size - g->pos);
+ proggendataflush(g);
+ proggenemit(g, insEnd);
+}
+
+
+// This function generates GC pointer info for global variables.
+static void
+proggenaddsym(ProgGen *g, LSym *s)
+{
+ LSym *gcprog;
+ uint8 *mask;
+ vlong i, size;
+
+ if(s->size == 0)
return;
- gotype = s->gotype;
- if(gotype != nil) {
- //print("gcaddsym: %s %d %s\n", s->name, s->size, gotype->name);
- adduintxx(ctxt, gc, GC_CALL, PtrSize);
- adduintxx(ctxt, gc, off, PtrSize);
- addpcrelplus(ctxt, gc, decodetype_gc(gotype), 3*PtrSize+4);
- if(PtrSize == 8)
- adduintxx(ctxt, gc, 0, 4);
- } else {
- //print("gcaddsym: %s %d <unknown type>\n", s->name, s->size);
- for(a = -off&(PtrSize-1); a+PtrSize<=s->size; a+=PtrSize) {
- adduintxx(ctxt, gc, GC_APTR, PtrSize);
- adduintxx(ctxt, gc, off+a, PtrSize);
+ // Skip alignment hole from the previous symbol.
+ proggenskip(g, g->pos, s->value - g->pos);
+ g->pos += s->value - g->pos;
+
+ if(s->gotype == nil && s->size >= PtrSize) {
+ // conservative scan
+ if((s->size%PtrSize) || (g->pos%PtrSize))
+ diag("proggenaddsym: unaligned symbol");
+ size = (s->size+PtrSize-1)/PtrSize*PtrSize;
+ if(size < 32*PtrSize) {
+ // Emit small symbols as data.
+ for(i = 0; i < size/PtrSize; i++)
+ proggendata(g, BitsPointer);
+ } else {
+ // Emit large symbols as array.
+ proggenarray(g, size/PtrSize);
+ proggendata(g, BitsPointer);
+ proggenarrayend(g);
+ }
+ g->pos = s->value + size;
+ } else if(s->gotype == nil || decodetype_noptr(s->gotype) || s->size < PtrSize) {
+ // no scan
+ if(s->size < 32*PtrSize) {
+ // Emit small symbols as data.
+ // This case also handles unaligned and tiny symbols, so tread carefully.
+ for(i = s->value; i < s->value+s->size; i++) {
+ if((i%PtrSize) == 0)
+ proggendata(g, BitsScalar);
+ }
+ } else {
+ // Emit large symbols as array.
+ if((s->size%PtrSize) || (g->pos%PtrSize))
+ diag("proggenaddsym: unaligned symbol");
+ proggenarray(g, s->size/PtrSize);
+ proggendata(g, BitsScalar);
+ proggenarrayend(g);
}
+ g->pos = s->value + s->size;
+ } else if(decodetype_usegcprog(s->gotype)) {
+ // gc program, copy directly
+ proggendataflush(g);
+ gcprog = decodetype_gcprog(s->gotype);
+ size = decodetype_size(s->gotype);
+ if((size%PtrSize) || (g->pos%PtrSize))
+ diag("proggenaddsym: unaligned symbol");
+ for(i = 0; i < gcprog->np-1; i++)
+ proggenemit(g, gcprog->p[i]);
+ g->pos = s->value + size;
+ } else {
+ // gc mask, it's small so emit as data
+ mask = decodetype_gcmask(s->gotype);
+ size = decodetype_size(s->gotype);
+ if((size%PtrSize) || (g->pos%PtrSize))
+ diag("proggenaddsym: unaligned symbol");
+ for(i = 0; i < size; i += PtrSize)
+ proggendata(g, (mask[i/PtrSize/2]>>((i/PtrSize%2)*4+2))&BitsMask);
+ g->pos = s->value + size;
}
}
@@ -755,19 +889,13 @@ dodata(void)
Section *sect;
Segment *segro;
LSym *s, *last, **l;
- LSym *gcdata1, *gcbss1;
+ LSym *gcdata, *gcbss;
+ ProgGen gen;
if(debug['v'])
Bprint(&bso, "%5.2f dodata\n", cputime());
Bflush(&bso);
- gcdata1 = linklookup(ctxt, "gcdata", 0);
- gcbss1 = linklookup(ctxt, "gcbss", 0);
-
- // size of .data and .bss section. the zero value is later replaced by the actual size of the section.
- adduintxx(ctxt, gcdata1, 0, PtrSize);
- adduintxx(ctxt, gcbss1, 0, PtrSize);
-
last = nil;
datap = nil;
@@ -884,6 +1012,8 @@ dodata(void)
sect->vaddr = datsize;
linklookup(ctxt, "data", 0)->sect = sect;
linklookup(ctxt, "edata", 0)->sect = sect;
+ gcdata = linklookup(ctxt, "gcdata", 0);
+ proggeninit(&gen, gcdata);
for(; s != nil && s->type < SBSS; s = s->next) {
if(s->type == SINITARR) {
ctxt->cursym = s;
@@ -893,13 +1023,11 @@ dodata(void)
s->type = SDATA;
datsize = aligndatsize(datsize, s);
s->value = datsize - sect->vaddr;
- gcaddsym(gcdata1, s, datsize - sect->vaddr); // gc
+ proggenaddsym(&gen, s); // gc
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
-
- adduintxx(ctxt, gcdata1, GC_END, PtrSize);
- setuintxx(ctxt, gcdata1, 0, sect->len, PtrSize);
+ proggenfini(&gen, sect->len); // gc
/* bss */
sect = addsection(&segdata, ".bss", 06);
@@ -908,17 +1036,17 @@ dodata(void)
sect->vaddr = datsize;
linklookup(ctxt, "bss", 0)->sect = sect;
linklookup(ctxt, "ebss", 0)->sect = sect;
+ gcbss = linklookup(ctxt, "gcbss", 0);
+ proggeninit(&gen, gcbss);
for(; s != nil && s->type < SNOPTRBSS; s = s->next) {
s->sect = sect;
datsize = aligndatsize(datsize, s);
s->value = datsize - sect->vaddr;
- gcaddsym(gcbss1, s, datsize - sect->vaddr); // gc
+ proggenaddsym(&gen, s); // gc
growdatsize(&datsize, s);
}
sect->len = datsize - sect->vaddr;
-
- adduintxx(ctxt, gcbss1, GC_END, PtrSize);
- setuintxx(ctxt, gcbss1, 0, sect->len, PtrSize);
+ proggenfini(&gen, sect->len); // gc
/* pointer-free bss */
sect = addsection(&segdata, ".noptrbss", 06);