Skip to content

Commit 7aa3031

Browse files
committed
cmd/gc: make liveness ~10x faster
1) The arrayindexof lookup function is O(n). Replace with O(1) lookups. 2) The checkptxt function is O(n²) and is purely for debugging. Only run when the debugging flags are turned on. 3) Iterating over sparse bitmaps can be done faster word by word. Introduce and use bvnext for that. Run times before and after, on my 2.5 GHz Core i5 MacBook Pro. x.go 9.48 0.84 issue 8259 x100.go 0.01 0.01 issue 8354 x1000.go 0.10 0.10 x2000.go 0.62 0.19 x3000.go 1.33 0.34 x4000.go 2.29 0.49 x5000.go 3.89 0.67 x6000.go 5.00 0.90 x7000.go 6.70 1.13 x8000.go 9.44 1.38 x9000.go 11.23 1.87 x10000.go 13.78 2.09 Fixes #8259. Fixes #8354. LGTM=iant, r R=golang-codereviews, iant, r CC=golang-codereviews https://golang.org/cl/125720043
1 parent c1fcdb0 commit 7aa3031

File tree

4 files changed

+69
-35
lines changed

4 files changed

+69
-35
lines changed

src/cmd/gc/array.c

-14
Original file line numberDiff line numberDiff line change
@@ -108,20 +108,6 @@ arrayadd(Array *array, void *element)
108108
arrayset(array, array->length - 1, element);
109109
}
110110

111-
int32
112-
arrayindexof(Array *array, void *element)
113-
{
114-
void *p;
115-
int32 i;
116-
117-
for(i = 0; i < array->length; i++) {
118-
p = arrayget(array, i);
119-
if(memcmp(p, &element, array->size) == 0)
120-
return i;
121-
}
122-
return -1;
123-
}
124-
125111
void
126112
arraysort(Array *array, int (*cmp)(const void*, const void*))
127113
{

src/cmd/gc/bv.c

+30-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
enum {
1010
WORDSIZE = sizeof(uint32),
1111
WORDBITS = 32,
12+
WORDMASK = WORDBITS - 1,
13+
WORDSHIFT = 5,
1214
};
1315

1416
static uintptr
@@ -94,13 +96,35 @@ bvconcat(Bvec *src1, Bvec *src2)
9496
int
9597
bvget(Bvec *bv, int32 i)
9698
{
97-
uint32 mask, word;
98-
9999
if(i < 0 || i >= bv->n)
100100
fatal("bvget: index %d is out of bounds with length %d\n", i, bv->n);
101-
mask = 1U << (i % WORDBITS);
102-
word = bv->b[i / WORDBITS] & mask;
103-
return word ? 1 : 0;
101+
return (bv->b[i>>WORDSHIFT] >> (i&WORDMASK)) & 1;
102+
}
103+
104+
// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
105+
// If there is no such index, bvnext returns -1.
106+
int
107+
bvnext(Bvec *bv, int32 i)
108+
{
109+
uint32 w;
110+
111+
// Jump i ahead to next word with bits.
112+
if((bv->b[i>>WORDSHIFT]>>(i&WORDMASK)) == 0) {
113+
i &= ~WORDMASK;
114+
i += WORDBITS;
115+
while(i < bv->n && bv->b[i>>WORDSHIFT] == 0)
116+
i += WORDBITS;
117+
}
118+
if(i >= bv->n)
119+
return -1;
120+
121+
// Find 1 bit.
122+
w = bv->b[i>>WORDSHIFT]>>(i&WORDMASK);
123+
while((w&1) == 0) {
124+
w>>=1;
125+
i++;
126+
}
127+
return i;
104128
}
105129

106130
int
@@ -109,7 +133,7 @@ bvisempty(Bvec *bv)
109133
int32 i;
110134

111135
for(i = 0; i < bv->n; i += WORDBITS)
112-
if(bv->b[i / WORDBITS] != 0)
136+
if(bv->b[i>>WORDSHIFT] != 0)
113137
return 0;
114138
return 1;
115139
}

src/cmd/gc/go.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,6 @@ int32 arraylength(Array *array);
10171017
void* arrayget(Array *array, int32 index);
10181018
void arrayset(Array *array, int32 index, void *element);
10191019
void arrayadd(Array *array, void *element);
1020-
int32 arrayindexof(Array* array, void *element);
10211020
void arraysort(Array* array, int (*cmp)(const void*, const void*));
10221021

10231022
/*
@@ -1043,6 +1042,7 @@ int bvcmp(Bvec *bv1, Bvec *bv2);
10431042
void bvcopy(Bvec *dst, Bvec *src);
10441043
Bvec* bvconcat(Bvec *src1, Bvec *src2);
10451044
int bvget(Bvec *bv, int32 i);
1045+
int32 bvnext(Bvec *bv, int32 i);
10461046
int bvisempty(Bvec *bv);
10471047
void bvnot(Bvec *bv);
10481048
void bvor(Bvec *dst, Bvec *src1, Bvec *src2);

src/cmd/gc/plive.c

+38-14
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,30 @@ getvariables(Node *fn)
283283
// For arguments and results, the bitmap covers all variables,
284284
// so we must include all the variables, even the ones without
285285
// pointers.
286+
//
287+
// The Node.opt field is available for use by optimization passes.
288+
// We use it to hold the index of the node in the variables array, plus 1
289+
// (so that 0 means the Node is not in the variables array).
290+
// Each pass should clear opt when done, but you never know,
291+
// so clear them all ourselves too.
292+
// The Node.curfn field is supposed to be set to the current function
293+
// already, but for some compiler-introduced names it seems not to be,
294+
// so fix that here.
295+
// Later, when we want to find the index of a node in the variables list,
296+
// we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1
297+
// is the index in the variables list.
298+
ll->n->opt = nil;
299+
ll->n->curfn = curfn;
286300
switch(ll->n->class) {
287301
case PAUTO:
288-
if(haspointers(ll->n->type))
302+
if(haspointers(ll->n->type)) {
303+
ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
289304
arrayadd(result, &ll->n);
305+
}
290306
break;
291307
case PPARAM:
292308
case PPARAMOUT:
309+
ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
293310
arrayadd(result, &ll->n);
294311
break;
295312
}
@@ -718,14 +735,16 @@ progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
718735
}
719736
if(info.flags & (LeftRead | LeftWrite | LeftAddr)) {
720737
from = &prog->from;
721-
if (from->node != nil && from->sym != nil) {
738+
if (from->node != nil && from->sym != nil && from->node->curfn == curfn) {
722739
switch(from->node->class & ~PHEAP) {
723740
case PAUTO:
724741
case PPARAM:
725742
case PPARAMOUT:
726-
pos = arrayindexof(vars, from->node);
743+
pos = (int)(uintptr)from->node->opt - 1; // index in vars
727744
if(pos == -1)
728745
goto Next;
746+
if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != from->node)
747+
fatal("bad bookkeeping in liveness %N %d", from->node, pos);
729748
if(from->node->addrtaken) {
730749
bvset(avarinit, pos);
731750
} else {
@@ -741,14 +760,16 @@ progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
741760
Next:
742761
if(info.flags & (RightRead | RightWrite | RightAddr)) {
743762
to = &prog->to;
744-
if (to->node != nil && to->sym != nil) {
763+
if (to->node != nil && to->sym != nil && to->node->curfn == curfn) {
745764
switch(to->node->class & ~PHEAP) {
746765
case PAUTO:
747766
case PPARAM:
748767
case PPARAMOUT:
749-
pos = arrayindexof(vars, to->node);
768+
pos = (int)(uintptr)to->node->opt - 1; // index in vars
750769
if(pos == -1)
751770
goto Next1;
771+
if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != to->node)
772+
fatal("bad bookkeeping in liveness %N %d", to->node, pos);
752773
if(to->node->addrtaken) {
753774
if(prog->as != AVARKILL)
754775
bvset(avarinit, pos);
@@ -1020,6 +1041,9 @@ checkptxt(Node *fn, Prog *firstp)
10201041
{
10211042
Prog *p;
10221043

1044+
if(debuglive == 0)
1045+
return;
1046+
10231047
for(p = firstp; p != P; p = p->link) {
10241048
if(0)
10251049
print("analyzing '%P'\n", p);
@@ -1172,21 +1196,17 @@ twobitlivepointermap(Liveness *lv, Bvec *liveout, Array *vars, Bvec *args, Bvec
11721196
vlong xoffset;
11731197
int32 i;
11741198

1175-
for(i = 0; i < arraylength(vars); i++) {
1199+
for(i = 0; (i = bvnext(liveout, i)) >= 0; i++) {
11761200
node = *(Node**)arrayget(vars, i);
11771201
switch(node->class) {
11781202
case PAUTO:
1179-
if(bvget(liveout, i)) {
1180-
xoffset = node->xoffset + stkptrsize;
1181-
twobitwalktype1(node->type, &xoffset, locals);
1182-
}
1203+
xoffset = node->xoffset + stkptrsize;
1204+
twobitwalktype1(node->type, &xoffset, locals);
11831205
break;
11841206
case PPARAM:
11851207
case PPARAMOUT:
1186-
if(bvget(liveout, i)) {
1187-
xoffset = node->xoffset;
1188-
twobitwalktype1(node->type, &xoffset, args);
1189-
}
1208+
xoffset = node->xoffset;
1209+
twobitwalktype1(node->type, &xoffset, args);
11901210
break;
11911211
}
11921212
}
@@ -1944,6 +1964,7 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
19441964
Array *cfg, *vars;
19451965
Liveness *lv;
19461966
int debugdelta;
1967+
NodeList *l;
19471968

19481969
// Change name to dump debugging information only for a specific function.
19491970
debugdelta = 0;
@@ -1984,6 +2005,9 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
19842005
twobitwritesymbol(lv->argslivepointers, argssym);
19852006

19862007
// Free everything.
2008+
for(l=fn->dcl; l != nil; l = l->next)
2009+
if(l->n != N)
2010+
l->n->opt = nil;
19872011
freeliveness(lv);
19882012
arrayfree(vars);
19892013
freecfg(cfg);

0 commit comments

Comments
 (0)