Skip to content

Commit 69dc3a9

Browse files
committed
[release-branch.go1.3] cmd/gc: make liveness ~10x faster
««« CL 125720043 / b92e5df7d3ba 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 »»» TBR=rsc CC=golang-codereviews https://golang.org/cl/121600043
1 parent 31f2f8d commit 69dc3a9

File tree

4 files changed

+69
-35
lines changed

4 files changed

+69
-35
lines changed

src/cmd/gc/array.c

Lines changed: 0 additions & 14 deletions
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

Lines changed: 30 additions & 6 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 38 additions & 14 deletions
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
}
@@ -1937,6 +1957,7 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
19371957
Array *cfg, *vars;
19381958
Liveness *lv;
19391959
int debugdelta;
1960+
NodeList *l;
19401961

19411962
// Change name to dump debugging information only for a specific function.
19421963
debugdelta = 0;
@@ -1977,6 +1998,9 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
19771998
twobitwritesymbol(lv->argslivepointers, argssym);
19781999

19792000
// Free everything.
2001+
for(l=fn->dcl; l != nil; l = l->next)
2002+
if(l->n != N)
2003+
l->n->opt = nil;
19802004
freeliveness(lv);
19812005
arrayfree(vars);
19822006
freecfg(cfg);

0 commit comments

Comments
 (0)