// in the arguments and locals area, indexed by bb->rpo.
Array *argslivepointers;
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*
// Collects and returns and array of Node*s for functions arguments and local
// variables.
static Array*
-getvariables(Node *fn, int allvalues)
+getvariables(Node *fn)
{
Array *result;
NodeList *ll;
case PAUTO:
case PPARAM:
case PPARAMOUT:
- if(haspointers(ll->n->type) || allvalues)
+ if(haspointers(ll->n->type))
arrayadd(result, &ll->n);
break;
}
// liveness computation. The cfg argument is an array of BasicBlock*s and the
// vars argument is an array of Node*s.
static Liveness*
-newliveness(Node *fn, Prog *ptxt, Array *cfg, Array *vars, int computedead)
+newliveness(Node *fn, Prog *ptxt, Array *cfg, Array *vars)
{
Liveness *result;
int32 i;
result->livepointers = 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;
}
free(*(Bvec**)arrayget(lv->argslivepointers, i));
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++) {
free(lv->uevar[i]);
free(lv->varkill[i]);
}
}
-
-// 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.
static Prog*
unlinkedprog(int as)
// over the block (as this loop does), while the liveout
// requires walking backward (as the next loop does).
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)
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.
// The TEXT instruction annotation is implicit.
if(p->as == ACALL) {
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
printbitset(int printed, char *name, Array *vars, Bvec *bits)
{
// words that are followed are the raw bitmap words. The arr argument is an
// array of Node*s.
static void
-twobitwritesymbol(Array *arr, Sym *sym, Bvec *check)
+twobitwritesymbol(Array *arr, Sym *sym)
{
Bvec *bv;
- int off, i, j, len, pos;
- uint32 bit, word, checkword;
+ int off, i, j, len;
+ uint32 word;
len = arraylength(arr);
- // Dump the length of the bitmap array.
- off = duint32(sym, 0, len);
+ off = 0;
+ 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++) {
+ // bitmap words
bv = *(Bvec**)arrayget(arr, i);
- // If we have been provided a check bitmap we can use it
- // to confirm that the bitmap we are dumping is a subset
- // 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.
+ if(bv == nil)
+ break;
for(j = 0; j < bv->n; j += 32) {
word = bv->b[j/32];
off = duint32(sym, off, word);
}
}
+ duint32(sym, 0, i); // number of bitmaps
ggloblsym(sym, off, 0, 1);
}
// the liveness of pointer variables in the function, and emits a runtime data
// structure read by the garbage collector.
void
-liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym, Sym *deadsym)
+liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
{
Array *cfg, *vars;
Liveness *lv;
cfg = newcfg(firstp);
if(debuglive >= 3)
printcfg(cfg);
- vars = getvariables(fn, deadsym != nil);
- lv = newliveness(fn, firstp, cfg, vars, deadsym != nil);
+ vars = getvariables(fn);
+ lv = newliveness(fn, firstp, cfg, vars);
// Run the dataflow framework.
livenessprologue(lv);
if(debuglive >= 3)
livenessprintcfg(lv);
livenessepilogue(lv);
-
+ if(debuglive >= 3)
+ livenessprintcfg(lv);
+ livenesscompact(lv);
+
if(debuglive >= 2)
livenessprintdebug(lv);
// Emit the live pointer map data structures
- twobitwritesymbol(lv->livepointers, livesym, nil);
- twobitwritesymbol(lv->argslivepointers, argssym, nil);
-
- // Optionally emit a dead value map data structure for locals.
- if(deadsym != nil)
- twobitwritesymbol(lv->deadvalues, deadsym, nil);
+ twobitwritesymbol(lv->livepointers, livesym);
+ twobitwritesymbol(lv->argslivepointers, argssym);
// Free everything.
freeliveness(lv);