This source file includes following definitions.
- xmalloc
- newblock
- freeblock
- addedge
- splicebefore
- printblock
- blockany
- getvariables
- printcfg
- reversepostorder
- blockrpocmp
- iscall
- isselectcommcasecall
- isnewselect
- isselectgocall
- isdeferreturn
- addselectgosucc
- fixselectgo
- newcfg
- freecfg
- isfunny
- progeffects
- newliveness
- freeliveness
- printeffects
- printnode
- printvars
- livenessprintblock
- livenessprintcfg
- checkauto
- checkparam
- checkprog
- checkptxt
- twobitwalktype1
- localswords
- argswords
- twobitlivepointermap
- unlinkedprog
- newpcdataprog
- issafepoint
- livenessprologue
- livenesssolve
- islive
- livenessepilogue
- hashbitmap
- livenesscompact
- printbitset
- livenessprintdebug
- twobitwritesymbol
- printprog
- liveness
#include <u.h>
#include <libc.h>
#include "gg.h"
#include "opt.h"
#include "../../pkg/runtime/funcdata.h"
enum { BitsPerPointer = 2 };
enum {
UNVISITED = 0,
VISITED = 1,
};
typedef struct BasicBlock BasicBlock;
struct BasicBlock {
Array *pred;
Array *succ;
Prog *first;
Prog *last;
int rpo;
int mark;
int lastbitmapindex;
};
typedef struct Liveness Liveness;
struct Liveness {
Node *fn;
Prog *ptxt;
Array *vars;
Array *cfg;
Bvec **uevar;
Bvec **varkill;
Bvec **livein;
Bvec **liveout;
Bvec **avarinit;
Bvec **avarinitany;
Bvec **avarinitall;
Array *argslivepointers;
Array *livepointers;
};
static void*
xmalloc(uintptr size)
{
void *result;
result = malloc(size);
if(result == nil)
fatal("malloc failed");
return result;
}
static BasicBlock*
newblock(Prog *prog)
{
BasicBlock *result;
if(prog == nil)
fatal("newblock: prog cannot be nil");
result = xmalloc(sizeof(*result));
result->rpo = -1;
result->mark = UNVISITED;
result->first = prog;
result->last = prog;
result->pred = arraynew(2, sizeof(BasicBlock*));
result->succ = arraynew(2, sizeof(BasicBlock*));
return result;
}
static void
freeblock(BasicBlock *bb)
{
if(bb == nil)
fatal("freeblock: cannot free nil");
arrayfree(bb->pred);
arrayfree(bb->succ);
free(bb);
}
static void
addedge(BasicBlock *from, BasicBlock *to)
{
if(from == nil)
fatal("addedge: from is nil");
if(to == nil)
fatal("addedge: to is nil");
arrayadd(from->succ, &to);
arrayadd(to->pred, &from);
}
static void
splicebefore(Liveness *lv, BasicBlock *bb, Prog *prev, Prog *curr)
{
Prog *next, tmp;
USED(lv);
tmp = *curr;
*curr = *prev;
curr->opt = tmp.opt;
curr->link = tmp.link;
next = prev;
*next = tmp;
next->opt = nil;
next->link = nil;
next->link = curr->link;
next->opt = curr;
curr->link = next;
if(next->link && next->link->opt == curr)
next->link->opt = next;
if(bb->last == curr)
bb->last = next;
}
static void
printblock(BasicBlock *bb)
{
BasicBlock *pred;
BasicBlock *succ;
Prog *prog;
int i;
print("basic block %d\n", bb->rpo);
print("\tpred:");
for(i = 0; i < arraylength(bb->pred); i++) {
pred = *(BasicBlock**)arrayget(bb->pred, i);
print(" %d", pred->rpo);
}
print("\n");
print("\tsucc:");
for(i = 0; i < arraylength(bb->succ); i++) {
succ = *(BasicBlock**)arrayget(bb->succ, i);
print(" %d", succ->rpo);
}
print("\n");
print("\tprog:\n");
for(prog = bb->first;; prog=prog->link) {
print("\t\t%P\n", prog);
if(prog == bb->last)
break;
}
}
static int
blockany(BasicBlock *bb, int (*callback)(Prog*))
{
Prog *p;
int result;
for(p = bb->last; p != nil; p = p->opt) {
result = (*callback)(p);
if(result != 0)
return result;
}
return 0;
}
static Array*
getvariables(Node *fn)
{
Array *result;
NodeList *ll;
result = arraynew(0, sizeof(Node*));
for(ll = fn->dcl; ll != nil; ll = ll->next) {
if(ll->n->op == ONAME) {
switch(ll->n->class) {
case PAUTO:
if(haspointers(ll->n->type))
arrayadd(result, &ll->n);
break;
case PPARAM:
case PPARAMOUT:
arrayadd(result, &ll->n);
break;
}
}
}
return result;
}
static void
printcfg(Array *cfg)
{
BasicBlock *bb;
int32 i;
for(i = 0; i < arraylength(cfg); i++) {
bb = *(BasicBlock**)arrayget(cfg, i);
printblock(bb);
}
}
static void
reversepostorder(BasicBlock *root, int32 *rpo)
{
BasicBlock *bb;
int i;
root->mark = VISITED;
for(i = 0; i < arraylength(root->succ); i++) {
bb = *(BasicBlock**)arrayget(root->succ, i);
if(bb->mark == UNVISITED)
reversepostorder(bb, rpo);
}
*rpo -= 1;
root->rpo = *rpo;
}
static int
blockrpocmp(const void *p1, const void *p2)
{
BasicBlock *bb1;
BasicBlock *bb2;
bb1 = *(BasicBlock**)p1;
bb2 = *(BasicBlock**)p2;
if(bb1->rpo < bb2->rpo)
return -1;
if(bb1->rpo > bb2->rpo)
return 1;
return 0;
}
static int
iscall(Prog *prog, LSym *name)
{
if(prog == nil)
fatal("iscall: prog is nil");
if(name == nil)
fatal("iscall: function name is nil");
if(prog->as != ACALL)
return 0;
return name == prog->to.sym;
}
static int
isselectcommcasecall(Prog *prog)
{
static LSym* names[5];
int32 i;
if(names[0] == nil) {
names[0] = linksym(pkglookup("selectsend", runtimepkg));
names[1] = linksym(pkglookup("selectrecv", runtimepkg));
names[2] = linksym(pkglookup("selectrecv2", runtimepkg));
names[3] = linksym(pkglookup("selectdefault", runtimepkg));
}
for(i = 0; names[i] != nil; i++)
if(iscall(prog, names[i]))
return 1;
return 0;
}
static int
isnewselect(Prog *prog)
{
static LSym *sym;
if(sym == nil)
sym = linksym(pkglookup("newselect", runtimepkg));
return iscall(prog, sym);
}
static int
isselectgocall(Prog *prog)
{
static LSym *sym;
if(sym == nil)
sym = linksym(pkglookup("selectgo", runtimepkg));
return iscall(prog, sym);
}
static int
isdeferreturn(Prog *prog)
{
static LSym *sym;
if(sym == nil)
sym = linksym(pkglookup("deferreturn", runtimepkg));
return iscall(prog, sym);
}
static void
addselectgosucc(BasicBlock *selectgo)
{
BasicBlock *pred;
BasicBlock *succ;
pred = selectgo;
for(;;) {
if(arraylength(pred->pred) == 0)
fatal("selectgo does not have a newselect");
pred = *(BasicBlock**)arrayget(pred->pred, 0);
if(blockany(pred, isselectcommcasecall)) {
if(arraylength(pred->succ) != 1)
fatal("select comm case has too many successors");
succ = *(BasicBlock**)arrayget(pred->succ, 0);
if(arraylength(succ->succ) != 2)
fatal("select comm case successor has too many successors");
addedge(selectgo, succ);
}
if(blockany(pred, isnewselect)) {
break;
}
}
}
static void
fixselectgo(Array *selectgo)
{
BasicBlock *bb;
int32 i;
for(i = 0; i < arraylength(selectgo); i++) {
bb = *(BasicBlock**)arrayget(selectgo, i);
addselectgosucc(bb);
}
}
static Array*
newcfg(Prog *firstp)
{
Prog *p;
Prog *prev;
BasicBlock *bb;
Array *cfg;
Array *selectgo;
int32 i;
int32 rpo;
for(p = firstp; p != P; p = p->link)
p->opt = nil;
selectgo = arraynew(0, sizeof(BasicBlock*));
cfg = arraynew(0, sizeof(BasicBlock*));
bb = newblock(firstp);
arrayadd(cfg, &bb);
for(p = firstp; p != P; p = p->link) {
if(p->to.type == D_BRANCH) {
if(p->to.u.branch == nil)
fatal("prog branch to nil");
if(p->to.u.branch->opt == nil) {
p->to.u.branch->opt = newblock(p->to.u.branch);
arrayadd(cfg, &p->to.u.branch->opt);
}
if(p->as != AJMP && p->link != nil && p->link->opt == nil) {
p->link->opt = newblock(p->link);
arrayadd(cfg, &p->link->opt);
}
} else if(isselectcommcasecall(p) || isselectgocall(p)) {
if(p->link->opt == nil) {
p->link->opt = newblock(p->link);
arrayadd(cfg, &p->link->opt);
}
}
}
for(i = 0; i < arraylength(cfg); i++) {
bb = *(BasicBlock**)arrayget(cfg, i);
for(p = bb->last; p != nil; p = p->link) {
if(p->opt != nil && p != bb->last)
break;
bb->last = p;
if(p->link != nil && p->link->as == ARET && p->link->mode == 1)
break;
if(isselectgocall(p))
arrayadd(selectgo, &bb);
}
if(bb->last->to.type == D_BRANCH)
addedge(bb, bb->last->to.u.branch->opt);
if(bb->last->link != nil) {
switch(bb->last->as) {
case AJMP:
case ARET:
case AUNDEF:
break;
default:
addedge(bb, bb->last->link->opt);
}
}
}
for(i = 0; i < arraylength(cfg); i++) {
bb = *(BasicBlock**)arrayget(cfg, i);
p = bb->first;
prev = nil;
for(;;) {
p->opt = prev;
if(p == bb->last)
break;
prev = p;
p = p->link;
}
}
if(arraylength(selectgo))
fixselectgo(selectgo);
arrayfree(selectgo);
for(i = 0; i < arraylength(cfg); i++) {
bb = *(BasicBlock**)arrayget(cfg, i);
bb->mark = UNVISITED;
}
bb = *(BasicBlock**)arrayget(cfg, 0);
rpo = arraylength(cfg);
reversepostorder(bb, &rpo);
arraysort(cfg, blockrpocmp);
bb = *(BasicBlock**)arrayget(cfg, 0);
if(bb->rpo == -1) {
print("newcfg: unreachable basic block for %P\n", bb->last);
printcfg(cfg);
fatal("newcfg: invalid control flow graph");
}
return cfg;
}
static void
freecfg(Array *cfg)
{
BasicBlock *bb;
BasicBlock *bb0;
Prog *p;
int32 i;
int32 len;
len = arraylength(cfg);
if(len > 0) {
bb0 = *(BasicBlock**)arrayget(cfg, 0);
for(p = bb0->first; p != P; p = p->link) {
p->opt = nil;
}
for(i = 0; i < len; i++) {
bb = *(BasicBlock**)arrayget(cfg, i);
freeblock(bb);
}
}
arrayfree(cfg);
}
static int
isfunny(Node *node)
{
char *names[] = { ".fp", ".args", nil };
int i;
if(node->sym != nil && node->sym->name != nil)
for(i = 0; names[i] != nil; i++)
if(strcmp(node->sym->name, names[i]) == 0)
return 1;
return 0;
}
static void
progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
{
ProgInfo info;
Adr *from;
Adr *to;
Node *node;
int32 i;
int32 pos;
bvresetall(uevar);
bvresetall(varkill);
bvresetall(avarinit);
proginfo(&info, prog);
if(prog->as == ARET) {
for(i = 0; i < arraylength(vars); i++) {
node = *(Node**)arrayget(vars, i);
switch(node->class & ~PHEAP) {
case PPARAM:
bvset(uevar, i);
break;
case PPARAMOUT:
if(!node->addrtaken && prog->to.type == D_NONE)
bvset(uevar, i);
break;
}
}
return;
}
if(prog->as == ATEXT) {
for(i = 0; i < arraylength(vars); i++) {
node = *(Node**)arrayget(vars, i);
switch(node->class & ~PHEAP) {
case PPARAM:
if(node->addrtaken)
bvset(avarinit, i);
bvset(varkill, i);
break;
}
}
return;
}
if(info.flags & (LeftRead | LeftWrite | LeftAddr)) {
from = &prog->from;
if (from->node != nil && from->sym != nil) {
switch(from->node->class & ~PHEAP) {
case PAUTO:
case PPARAM:
case PPARAMOUT:
pos = arrayindexof(vars, from->node);
if(pos == -1)
goto Next;
if(from->node->addrtaken) {
bvset(avarinit, pos);
} else {
if(info.flags & (LeftRead | LeftAddr))
bvset(uevar, pos);
if(info.flags & LeftWrite)
if(from->node != nil && !isfat(from->node->type))
bvset(varkill, pos);
}
}
}
}
Next:
if(info.flags & (RightRead | RightWrite | RightAddr)) {
to = &prog->to;
if (to->node != nil && to->sym != nil) {
switch(to->node->class & ~PHEAP) {
case PAUTO:
case PPARAM:
case PPARAMOUT:
pos = arrayindexof(vars, to->node);
if(pos == -1)
goto Next1;
if(to->node->addrtaken) {
if(prog->as != AVARKILL)
bvset(avarinit, pos);
if(prog->as == AVARDEF || prog->as == AVARKILL)
bvset(varkill, pos);
} else {
if((info.flags & RightRead) || (info.flags & (RightAddr|RightWrite)) == RightAddr)
bvset(uevar, pos);
if(info.flags & RightWrite)
if(to->node != nil && (!isfat(to->node->type) || prog->as == AVARDEF))
bvset(varkill, pos);
}
}
}
}
Next1:;
}
static Liveness*
newliveness(Node *fn, Prog *ptxt, Array *cfg, Array *vars)
{
Liveness *result;
int32 i;
int32 nblocks;
int32 nvars;
result = xmalloc(sizeof(*result));
result->fn = fn;
result->ptxt = ptxt;
result->cfg = cfg;
result->vars = vars;
nblocks = arraylength(cfg);
result->uevar = xmalloc(sizeof(Bvec*) * nblocks);
result->varkill = xmalloc(sizeof(Bvec*) * nblocks);
result->livein = xmalloc(sizeof(Bvec*) * nblocks);
result->liveout = xmalloc(sizeof(Bvec*) * nblocks);
result->avarinit = xmalloc(sizeof(Bvec*) * nblocks);
result->avarinitany = xmalloc(sizeof(Bvec*) * nblocks);
result->avarinitall = xmalloc(sizeof(Bvec*) * nblocks);
nvars = arraylength(vars);
for(i = 0; i < nblocks; i++) {
result->uevar[i] = bvalloc(nvars);
result->varkill[i] = bvalloc(nvars);
result->livein[i] = bvalloc(nvars);
result->liveout[i] = bvalloc(nvars);
result->avarinit[i] = bvalloc(nvars);
result->avarinitany[i] = bvalloc(nvars);
result->avarinitall[i] = bvalloc(nvars);
}
result->livepointers = arraynew(0, sizeof(Bvec*));
result->argslivepointers = arraynew(0, sizeof(Bvec*));
return result;
}
static void
freeliveness(Liveness *lv)
{
int32 i;
if(lv == nil)
fatal("freeliveness: cannot free nil");
for(i = 0; i < arraylength(lv->livepointers); i++)
free(*(Bvec**)arrayget(lv->livepointers, i));
arrayfree(lv->livepointers);
for(i = 0; i < arraylength(lv->argslivepointers); i++)
free(*(Bvec**)arrayget(lv->argslivepointers, i));
arrayfree(lv->argslivepointers);
for(i = 0; i < arraylength(lv->cfg); i++) {
free(lv->uevar[i]);
free(lv->varkill[i]);
free(lv->livein[i]);
free(lv->liveout[i]);
free(lv->avarinit[i]);
free(lv->avarinitany[i]);
free(lv->avarinitall[i]);
}
free(lv->uevar);
free(lv->varkill);
free(lv->livein);
free(lv->liveout);
free(lv->avarinit);
free(lv->avarinitany);
free(lv->avarinitall);
free(lv);
}
static void
printeffects(Prog *p, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
{
print("effects of %P", p);
print("\nuevar: ");
bvprint(uevar);
print("\nvarkill: ");
bvprint(varkill);
print("\navarinit: ");
bvprint(avarinit);
print("\n");
}
static void
printnode(Node *node)
{
char *p;
char *a;
p = haspointers(node->type) ? "^" : "";
a = node->addrtaken ? "@" : "";
print(" %N%s%s", node, p, a);
}
static void
printvars(char *name, Bvec *bv, Array *vars)
{
int32 i;
print("%s:", name);
for(i = 0; i < arraylength(vars); i++)
if(bvget(bv, i))
printnode(*(Node**)arrayget(vars, i));
print("\n");
}
static void
livenessprintblock(Liveness *lv, BasicBlock *bb)
{
BasicBlock *pred;
BasicBlock *succ;
Prog *prog;
Bvec *live;
int i;
int32 pos;
print("basic block %d\n", bb->rpo);
print("\tpred:");
for(i = 0; i < arraylength(bb->pred); i++) {
pred = *(BasicBlock**)arrayget(bb->pred, i);
print(" %d", pred->rpo);
}
print("\n");
print("\tsucc:");
for(i = 0; i < arraylength(bb->succ); i++) {
succ = *(BasicBlock**)arrayget(bb->succ, i);
print(" %d", succ->rpo);
}
print("\n");
printvars("\tuevar", lv->uevar[bb->rpo], lv->vars);
printvars("\tvarkill", lv->varkill[bb->rpo], lv->vars);
printvars("\tlivein", lv->livein[bb->rpo], lv->vars);
printvars("\tliveout", lv->liveout[bb->rpo], lv->vars);
printvars("\tavarinit", lv->avarinit[bb->rpo], lv->vars);
printvars("\tavarinitany", lv->avarinitany[bb->rpo], lv->vars);
printvars("\tavarinitall", lv->avarinitall[bb->rpo], lv->vars);
print("\tprog:\n");
for(prog = bb->first;; prog = prog->link) {
print("\t\t%P", prog);
if(prog->as == APCDATA && prog->from.offset == PCDATA_StackMapIndex) {
pos = prog->to.offset;
live = *(Bvec**)arrayget(lv->livepointers, pos);
print(" ");
bvprint(live);
}
print("\n");
if(prog == bb->last)
break;
}
}
static void
livenessprintcfg(Liveness *lv)
{
BasicBlock *bb;
int32 i;
for(i = 0; i < arraylength(lv->cfg); i++) {
bb = *(BasicBlock**)arrayget(lv->cfg, i);
livenessprintblock(lv, bb);
}
}
static void
checkauto(Node *fn, Prog *p, Node *n)
{
NodeList *l;
for(l = fn->dcl; l != nil; l = l->next)
if(l->n->op == ONAME && l->n->class == PAUTO && l->n == n)
return;
print("checkauto %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
for(l = fn->dcl; l != nil; l = l->next)
print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
yyerror("checkauto: invariant lost");
}
static void
checkparam(Node *fn, Prog *p, Node *n)
{
NodeList *l;
Node *a;
int class;
if(isfunny(n))
return;
for(l = fn->dcl; l != nil; l = l->next) {
a = l->n;
class = a->class & ~PHEAP;
if(a->op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n)
return;
}
print("checkparam %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
for(l = fn->dcl; l != nil; l = l->next)
print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
yyerror("checkparam: invariant lost");
}
static void
checkprog(Node *fn, Prog *p)
{
if(p->from.type == D_AUTO)
checkauto(fn, p, p->from.node);
if(p->from.type == D_PARAM)
checkparam(fn, p, p->from.node);
if(p->to.type == D_AUTO)
checkauto(fn, p, p->to.node);
if(p->to.type == D_PARAM)
checkparam(fn, p, p->to.node);
}
static void
checkptxt(Node *fn, Prog *firstp)
{
Prog *p;
for(p = firstp; p != P; p = p->link) {
if(0)
print("analyzing '%P'\n", p);
switch(p->as) {
case ADATA:
case AGLOBL:
case ANAME:
case ASIGNAME:
case ATYPE:
continue;
}
checkprog(fn, p);
}
}
static void
twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
{
vlong fieldoffset;
vlong i;
vlong o;
Type *t1;
if(t->align > 0 && (*xoffset & (t->align - 1)) != 0)
fatal("twobitwalktype1: invalid initial alignment, %T", t);
switch(t->etype) {
case TINT8:
case TUINT8:
case TINT16:
case TUINT16:
case TINT32:
case TUINT32:
case TINT64:
case TUINT64:
case TINT:
case TUINT:
case TUINTPTR:
case TBOOL:
case TFLOAT32:
case TFLOAT64:
case TCOMPLEX64:
case TCOMPLEX128:
for(i = 0; i < t->width; i++) {
bvset(bv, ((*xoffset + i) / widthptr) * BitsPerPointer);
}
*xoffset += t->width;
break;
case TPTR32:
case TPTR64:
case TUNSAFEPTR:
case TFUNC:
case TCHAN:
case TMAP:
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid alignment, %T", t);
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1);
*xoffset += t->width;
break;
case TSTRING:
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid alignment, %T", t);
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0);
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1);
*xoffset += t->width;
break;
case TINTER:
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid alignment, %T", t);
bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 0);
bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1);
if(isnilinter(t)) {
bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 2);
bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
} else {
bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
}
*xoffset += t->width;
break;
case TARRAY:
if(t->bound < -1)
fatal("twobitwalktype1: invalid bound, %T", t);
if(isslice(t)) {
if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid TARRAY alignment, %T", t);
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0);
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1);
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 2);
*xoffset += t->width;
} else
for(i = 0; i < t->bound; i++)
twobitwalktype1(t->type, xoffset, bv);
break;
case TSTRUCT:
o = 0;
for(t1 = t->type; t1 != T; t1 = t1->down) {
fieldoffset = t1->width;
*xoffset += fieldoffset - o;
twobitwalktype1(t1->type, xoffset, bv);
o = fieldoffset + t1->type->width;
}
*xoffset += t->width - o;
break;
default:
fatal("twobitwalktype1: unexpected type, %T", t);
}
}
static int32
localswords(void)
{
return stkptrsize / widthptr;
}
static int32
argswords(void)
{
return curfn->type->argwid / widthptr;
}
static void
twobitlivepointermap(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;
}
}
thisargtype = getthisx(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);
}
}
static Prog*
unlinkedprog(int as)
{
Prog *p;
p = mal(sizeof(*p));
clearp(p);
p->as = as;
return p;
}
static Prog*
newpcdataprog(Prog *prog, int32 index)
{
Node from, to;
Prog *pcdata;
nodconst(&from, types[TINT32], PCDATA_StackMapIndex);
nodconst(&to, types[TINT32], index);
pcdata = unlinkedprog(APCDATA);
pcdata->lineno = prog->lineno;
naddr(&from, &pcdata->from, 0);
naddr(&to, &pcdata->to, 0);
return pcdata;
}
static int
issafepoint(Prog *prog)
{
return prog->as == ATEXT || prog->as == ACALL;
}
static void
livenessprologue(Liveness *lv)
{
BasicBlock *bb;
Bvec *uevar, *varkill, *avarinit;
Prog *p;
int32 i;
int32 nvars;
nvars = arraylength(lv->vars);
uevar = bvalloc(nvars);
varkill = bvalloc(nvars);
avarinit = bvalloc(nvars);
for(i = 0; i < arraylength(lv->cfg); i++) {
bb = *(BasicBlock**)arrayget(lv->cfg, i);
for(p = bb->last; p != nil; p = p->opt) {
progeffects(p, lv->vars, uevar, varkill, avarinit);
if(debuglive >= 3)
printeffects(p, uevar, varkill, avarinit);
bvor(lv->varkill[i], lv->varkill[i], varkill);
bvandnot(lv->uevar[i], lv->uevar[i], varkill);
bvor(lv->uevar[i], lv->uevar[i], uevar);
}
bvresetall(varkill);
for(p = bb->first;; p = p->link) {
progeffects(p, lv->vars, uevar, varkill, avarinit);
if(debuglive >= 3)
printeffects(p, uevar, varkill, avarinit);
bvandnot(lv->avarinit[i], lv->avarinit[i], varkill);
bvor(lv->avarinit[i], lv->avarinit[i], avarinit);
if(p == bb->last)
break;
}
}
free(uevar);
free(varkill);
free(avarinit);
}
static void
livenesssolve(Liveness *lv)
{
BasicBlock *bb, *succ, *pred;
Bvec *newlivein, *newliveout, *any, *all;
int32 rpo, i, j, change;
newlivein = bvalloc(arraylength(lv->vars));
newliveout = bvalloc(arraylength(lv->vars));
any = bvalloc(arraylength(lv->vars));
all = bvalloc(arraylength(lv->vars));
for(i = 0; i < arraylength(lv->cfg); i++) {
bb = *(BasicBlock**)arrayget(lv->cfg, i);
rpo = bb->rpo;
if(i == 0)
bvcopy(lv->avarinitall[rpo], lv->avarinit[rpo]);
else {
bvresetall(lv->avarinitall[rpo]);
bvnot(lv->avarinitall[rpo]);
}
bvcopy(lv->avarinitany[rpo], lv->avarinit[rpo]);
}
change = 1;
while(change != 0) {
change = 0;
for(i = 0; i < arraylength(lv->cfg); i++) {
bb = *(BasicBlock**)arrayget(lv->cfg, i);
rpo = bb->rpo;
bvresetall(any);
bvresetall(all);
for(j = 0; j < arraylength(bb->pred); j++) {
pred = *(BasicBlock**)arrayget(bb->pred, j);
if(j == 0) {
bvcopy(any, lv->avarinitany[pred->rpo]);
bvcopy(all, lv->avarinitall[pred->rpo]);
} else {
bvor(any, any, lv->avarinitany[pred->rpo]);
bvand(all, all, lv->avarinitall[pred->rpo]);
}
}
bvandnot(any, any, lv->varkill[rpo]);
bvandnot(all, all, lv->varkill[rpo]);
bvor(any, any, lv->avarinit[rpo]);
bvor(all, all, lv->avarinit[rpo]);
if(bvcmp(any, lv->avarinitany[rpo])) {
change = 1;
bvcopy(lv->avarinitany[rpo], any);
}
if(bvcmp(all, lv->avarinitall[rpo])) {
change = 1;
bvcopy(lv->avarinitall[rpo], all);
}
}
}
change = 1;
while(change != 0) {
change = 0;
for(i = arraylength(lv->cfg) - 1; i >= 0; i--) {
bb = *(BasicBlock**)arrayget(lv->cfg, i);
rpo = bb->rpo;
bvresetall(newliveout);
for(j = 0; j < arraylength(bb->succ); j++) {
succ = *(BasicBlock**)arrayget(bb->succ, j);
bvor(newliveout, newliveout, lv->livein[succ->rpo]);
}
if(bvcmp(lv->liveout[rpo], newliveout)) {
change = 1;
bvcopy(lv->liveout[rpo], newliveout);
}
bvandnot(newlivein, lv->liveout[rpo], lv->varkill[rpo]);
bvor(lv->livein[rpo], newlivein, lv->uevar[rpo]);
}
}
free(newlivein);
free(newliveout);
free(any);
free(all);
}
static int
islive(Node *n, Bvec *args, Bvec *locals)
{
int i;
switch(n->class) {
case PPARAM:
case PPARAMOUT:
for(i = 0; i < n->type->width/widthptr*BitsPerPointer; i++)
if(bvget(args, n->xoffset/widthptr*BitsPerPointer + i))
return 1;
break;
case PAUTO:
for(i = 0; i < n->type->width/widthptr*BitsPerPointer; i++)
if(bvget(locals, (n->xoffset + stkptrsize)/widthptr*BitsPerPointer + i))
return 1;
break;
}
return 0;
}
static void
livenessepilogue(Liveness *lv)
{
BasicBlock *bb, *pred;
Bvec *ambig, *livein, *liveout, *uevar, *varkill, *args, *locals, *avarinit, *any, *all;
Node *n;
Prog *p, *next;
int32 i, j, numlive, startmsg, nmsg, nvars, pos;
vlong xoffset;
char **msg;
Fmt fmt;
nvars = arraylength(lv->vars);
livein = bvalloc(nvars);
liveout = bvalloc(nvars);
uevar = bvalloc(nvars);
varkill = bvalloc(nvars);
avarinit = bvalloc(nvars);
any = bvalloc(nvars);
all = bvalloc(nvars);
ambig = bvalloc(localswords() * BitsPerPointer);
msg = nil;
nmsg = 0;
startmsg = 0;
for(i = 0; i < arraylength(lv->cfg); i++) {
bb = *(BasicBlock**)arrayget(lv->cfg, i);
bvresetall(any);
bvresetall(all);
for(j = 0; j < arraylength(bb->pred); j++) {
pred = *(BasicBlock**)arrayget(bb->pred, j);
if(j == 0) {
bvcopy(any, lv->avarinitany[pred->rpo]);
bvcopy(all, lv->avarinitall[pred->rpo]);
} else {
bvor(any, any, lv->avarinitany[pred->rpo]);
bvand(all, all, lv->avarinitall[pred->rpo]);
}
}
for(p = bb->first;; p = p->link) {
progeffects(p, lv->vars, uevar, varkill, avarinit);
bvandnot(any, any, varkill);
bvandnot(all, all, varkill);
bvor(any, any, avarinit);
bvor(all, all, avarinit);
if(issafepoint(p)) {
if(precisestack_enabled) {
bvresetall(livein);
bvandnot(liveout, any, all);
if(!bvisempty(liveout)) {
for(pos = 0; pos < liveout->n; pos++) {
if(!bvget(liveout, pos))
continue;
bvset(all, pos);
n = *(Node**)arrayget(lv->vars, pos);
if(!n->needzero) {
n->needzero = 1;
if(debuglive >= 1)
warnl(p->lineno, "%N: %lN is ambiguously live", curfn->nname, n);
xoffset = n->xoffset + stkptrsize;
twobitwalktype1(n->type, &xoffset, ambig);
}
}
}
}
args = bvalloc(argswords() * BitsPerPointer);
arrayadd(lv->argslivepointers, &args);
locals = bvalloc(localswords() * BitsPerPointer);
arrayadd(lv->livepointers, &locals);
if(debuglive >= 3) {
print("%P\n", p);
printvars("avarinitany", any, lv->vars);
}
twobitlivepointermap(lv, any, lv->vars, args, locals);
}
if(p == bb->last)
break;
}
bb->lastbitmapindex = arraylength(lv->livepointers) - 1;
}
for(i = 0; i < arraylength(lv->cfg); i++) {
bb = *(BasicBlock**)arrayget(lv->cfg, i);
if(debuglive >= 1 && strcmp(curfn->nname->sym->name, "init") != 0 && curfn->nname->sym->name[0] != '.') {
nmsg = arraylength(lv->livepointers);
startmsg = nmsg;
msg = xmalloc(nmsg*sizeof msg[0]);
for(j=0; j<nmsg; j++)
msg[j] = nil;
}
pos = bb->lastbitmapindex;
if(pos < 0) {
fatal("livenessepilogue");
}
bvcopy(livein, lv->liveout[bb->rpo]);
for(p = bb->last; p != nil; p = next) {
next = p->opt;
progeffects(p, lv->vars, uevar, varkill, avarinit);
bvcopy(liveout, livein);
bvandnot(livein, liveout, varkill);
bvor(livein, livein, uevar);
if(debuglive >= 3 && issafepoint(p)){
print("%P\n", p);
printvars("uevar", uevar, lv->vars);
printvars("varkill", varkill, lv->vars);
printvars("livein", livein, lv->vars);
printvars("liveout", liveout, lv->vars);
}
if(issafepoint(p)) {
if(p->as == ATEXT) {
for(j = 0; j < liveout->n; j++) {
if(!bvget(liveout, j))
continue;
n = *(Node**)arrayget(lv->vars, j);
if(n->class != PPARAM)
yyerrorl(p->lineno, "internal error: %N %lN recorded as live on entry", curfn->nname, n);
}
}
args = *(Bvec**)arrayget(lv->argslivepointers, pos);
locals = *(Bvec**)arrayget(lv->livepointers, pos);
twobitlivepointermap(lv, liveout, lv->vars, args, locals);
if(p->as == ACALL)
bvor(locals, locals, ambig);
if(msg != nil) {
fmtstrinit(&fmt);
fmtprint(&fmt, "%L: live at ", p->lineno);
if(p->as == ACALL && p->to.node)
fmtprint(&fmt, "call to %s:", p->to.node->sym->name);
else if(p->as == ACALL)
fmtprint(&fmt, "indirect call:");
else
fmtprint(&fmt, "entry to %s:", p->from.node->sym->name);
numlive = 0;
for(j = 0; j < arraylength(lv->vars); j++) {
n = *(Node**)arrayget(lv->vars, j);
if(islive(n, args, locals)) {
fmtprint(&fmt, " %N", n);
numlive++;
}
}
fmtprint(&fmt, "\n");
if(numlive == 0)
free(fmtstrflush(&fmt));
else
msg[--startmsg] = fmtstrflush(&fmt);
}
if(p->as == ACALL) {
if(isdeferreturn(p)) {
splicebefore(lv, bb, newpcdataprog(p->opt, pos), p->opt);
} else {
splicebefore(lv, bb, newpcdataprog(p, pos), p);
}
}
pos--;
}
}
if(msg != nil) {
for(j=startmsg; j<nmsg; j++)
if(msg[j] != nil)
print("%s", msg[j]);
free(msg);
msg = nil;
nmsg = 0;
startmsg = 0;
}
}
free(livein);
free(liveout);
free(uevar);
free(varkill);
free(avarinit);
free(any);
free(all);
free(ambig);
flusherrors();
}
#define H0 2166136261UL
#define Hp 16777619UL
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;
}
static void
livenesscompact(Liveness *lv)
{
int *table, *remap, i, j, n, tablesize, uniq;
uint32 h;
Bvec *local, *arg, *jlocal, *jarg;
Prog *p;
n = arraylength(lv->livepointers);
tablesize = 4*n;
table = xmalloc(tablesize*sizeof table[0]);
memset(table, 0xff, tablesize*sizeof table[0]);
remap = xmalloc(n*sizeof remap[0]);
memset(remap, 0xff, n*sizeof remap[0]);
uniq = 0;
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:;
}
for(j=uniq; j<n; j++) {
*(Bvec**)arrayget(lv->livepointers, j) = nil;
*(Bvec**)arrayget(lv->argslivepointers, j) = nil;
}
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)
{
int i, started;
Node *n;
started = 0;
for(i=0; i<arraylength(vars); i++) {
if(!bvget(bits, i))
continue;
if(!started) {
if(!printed)
print("\t");
else
print(" ");
started = 1;
printed = 1;
print("%s=", name);
} else {
print(",");
}
n = *(Node**)arrayget(vars, i);
print("%s", n->sym->name);
}
return printed;
}
static void
livenessprintdebug(Liveness *lv)
{
int i, j, pcdata, printed;
BasicBlock *bb;
Prog *p;
Bvec *uevar, *varkill, *avarinit, *args, *locals;
Node *n;
print("liveness: %s\n", curfn->nname->sym->name);
uevar = bvalloc(arraylength(lv->vars));
varkill = bvalloc(arraylength(lv->vars));
avarinit = bvalloc(arraylength(lv->vars));
pcdata = 0;
for(i = 0; i < arraylength(lv->cfg); i++) {
if(i > 0)
print("\n");
bb = *(BasicBlock**)arrayget(lv->cfg, i);
print("bb#%d pred=", i);
for(j = 0; j < arraylength(bb->pred); j++) {
if(j > 0)
print(",");
print("%d", (*(BasicBlock**)arrayget(bb->pred, j))->rpo);
}
print(" succ=");
for(j = 0; j < arraylength(bb->succ); j++) {
if(j > 0)
print(",");
print("%d", (*(BasicBlock**)arrayget(bb->succ, j))->rpo);
}
print("\n");
printed = 0;
printed = printbitset(printed, "uevar", lv->vars, lv->uevar[bb->rpo]);
printed = printbitset(printed, "livein", lv->vars, lv->livein[bb->rpo]);
if(printed)
print("\n");
for(p = bb->first;; p = p->link) {
print("%P\n", p);
if(p->as == APCDATA && p->from.offset == PCDATA_StackMapIndex)
pcdata = p->to.offset;
progeffects(p, lv->vars, uevar, varkill, avarinit);
printed = 0;
printed = printbitset(printed, "uevar", lv->vars, uevar);
printed = printbitset(printed, "varkill", lv->vars, varkill);
printed = printbitset(printed, "avarinit", lv->vars, avarinit);
if(printed)
print("\n");
if(issafepoint(p)) {
args = *(Bvec**)arrayget(lv->argslivepointers, pcdata);
locals = *(Bvec**)arrayget(lv->livepointers, pcdata);
print("\tlive=");
printed = 0;
for(j = 0; j < arraylength(lv->vars); j++) {
n = *(Node**)arrayget(lv->vars, j);
if(islive(n, args, locals)) {
if(printed++)
print(",");
print("%N", n);
}
}
print("\n");
}
if(p == bb->last)
break;
}
print("end\n");
printed = printbitset(printed, "varkill", lv->vars, lv->varkill[bb->rpo]);
printed = printbitset(printed, "liveout", lv->vars, lv->liveout[bb->rpo]);
printed = printbitset(printed, "avarinit", lv->vars, lv->avarinit[bb->rpo]);
printed = printbitset(printed, "avarinitany", lv->vars, lv->avarinitany[bb->rpo]);
printed = printbitset(printed, "avarinitall", lv->vars, lv->avarinitall[bb->rpo]);
if(printed)
print("\n");
}
print("\n");
free(uevar);
free(varkill);
free(avarinit);
}
static void
twobitwritesymbol(Array *arr, Sym *sym)
{
Bvec *bv;
int off, i, j, len;
uint32 word;
len = arraylength(arr);
off = 0;
off += 4;
bv = *(Bvec**)arrayget(arr, 0);
off = duint32(sym, off, bv->n);
for(i = 0; i < len; i++) {
bv = *(Bvec**)arrayget(arr, i);
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);
ggloblsym(sym, off, 0, 1);
}
static void
printprog(Prog *p)
{
while(p != nil) {
print("%P\n", p);
p = p->link;
}
}
void
liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
{
Array *cfg, *vars;
Liveness *lv;
int debugdelta;
debugdelta = 0;
if(strcmp(curfn->nname->sym->name, "!") == 0)
debugdelta = 2;
debuglive += debugdelta;
if(debuglive >= 3) {
print("liveness: %s\n", curfn->nname->sym->name);
printprog(firstp);
}
checkptxt(fn, firstp);
cfg = newcfg(firstp);
if(debuglive >= 3)
printcfg(cfg);
vars = getvariables(fn);
lv = newliveness(fn, firstp, cfg, vars);
livenessprologue(lv);
if(debuglive >= 3)
livenessprintcfg(lv);
livenesssolve(lv);
if(debuglive >= 3)
livenessprintcfg(lv);
livenessepilogue(lv);
if(debuglive >= 3)
livenessprintcfg(lv);
livenesscompact(lv);
if(debuglive >= 2)
livenessprintdebug(lv);
twobitwritesymbol(lv->livepointers, livesym);
twobitwritesymbol(lv->argslivepointers, argssym);
freeliveness(lv);
arrayfree(vars);
freecfg(cfg);
debuglive -= debugdelta;
}