This source file includes following definitions.
- noreturn
- chasejmp
- mark
- fixjmp
- flowstart
- flowend
- postorder
- rpolca
- doms
- loophead
- loopmark
- flowrpo
- uniqp
- uniqs
- startcmp
- canmerge
- mergetemp
- mergewalk
- varkillwalk
- nilopt
- nilwalkback
- nilwalkfwd
#include <u.h>
#include <libc.h>
#include "gg.h"
#include "opt.h"
int
noreturn(Prog *p)
{
Sym *s;
int i;
static Sym* symlist[10];
if(symlist[0] == S) {
symlist[0] = pkglookup("panicindex", runtimepkg);
symlist[1] = pkglookup("panicslice", runtimepkg);
symlist[2] = pkglookup("throwinit", runtimepkg);
symlist[3] = pkglookup("panic", runtimepkg);
symlist[4] = pkglookup("panicwrap", runtimepkg);
symlist[5] = pkglookup("throwreturn", runtimepkg);
symlist[6] = pkglookup("selectgo", runtimepkg);
symlist[7] = pkglookup("block", runtimepkg);
}
if(p->to.node == nil)
return 0;
s = p->to.node->sym;
if(s == S)
return 0;
for(i=0; symlist[i]!=S; i++)
if(s == symlist[i])
return 1;
return 0;
}
static Prog*
chasejmp(Prog *p, int *jmploop)
{
int n;
n = 0;
while(p != P && p->as == AJMP && p->to.type == D_BRANCH) {
if(++n > 10) {
*jmploop = 1;
break;
}
p = p->to.u.branch;
}
return p;
}
#define alive ((void*)0)
#define dead ((void*)1)
static void
mark(Prog *firstp)
{
Prog *p;
for(p=firstp; p; p=p->link) {
if(p->opt != dead)
break;
p->opt = alive;
if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch)
mark(p->to.u.branch);
if(p->as == AJMP || p->as == ARET || p->as == AUNDEF)
break;
}
}
void
fixjmp(Prog *firstp)
{
int jmploop;
Prog *p, *last;
if(debug['R'] && debug['v'])
print("\nfixjmp\n");
jmploop = 0;
for(p=firstp; p; p=p->link) {
if(debug['R'] && debug['v'])
print("%P\n", p);
if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) {
p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
if(debug['R'] && debug['v'])
print("->%P\n", p);
}
p->opt = dead;
}
if(debug['R'] && debug['v'])
print("\n");
mark(firstp);
last = nil;
for(p=firstp; p; p=p->link) {
if(p->opt == dead) {
if(p->link == P && p->as == ARET && last && last->as != ARET) {
p->mode = 1;
} else {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
}
if(last)
last->link = p;
last = p;
}
last->link = P;
if(!jmploop) {
last = nil;
for(p=firstp; p; p=p->link) {
if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
if(debug['R'] && debug['v'])
print("del %P\n", p);
continue;
}
if(last)
last->link = p;
last = p;
}
last->link = P;
}
if(debug['R'] && debug['v']) {
print("\n");
for(p=firstp; p; p=p->link)
print("%P\n", p);
print("\n");
}
}
#undef alive
#undef dead
Graph*
flowstart(Prog *firstp, int size)
{
int nf;
Flow *f, *f1, *start, *last;
Graph *graph;
Prog *p;
ProgInfo info;
nf = 0;
for(p = firstp; p != P; p = p->link) {
p->opt = nil;
proginfo(&info, p);
if(info.flags & Skip)
continue;
p->opt = (void*)1;
nf++;
}
if(nf == 0)
return nil;
if(nf >= 20000) {
return nil;
}
graph = calloc(sizeof *graph + size*nf, 1);
if(graph == nil)
fatal("out of memory");
start = (Flow*)(graph+1);
last = nil;
f = start;
for(p = firstp; p != P; p = p->link) {
if(p->opt == nil)
continue;
p->opt = f;
f->prog = p;
if(last)
last->link = f;
last = f;
f = (Flow*)((uchar*)f + size);
}
for(f = start; f != nil; f = f->link) {
p = f->prog;
proginfo(&info, p);
if(!(info.flags & Break)) {
f1 = f->link;
f->s1 = f1;
f1->p1 = f;
}
if(p->to.type == D_BRANCH) {
if(p->to.u.branch == P)
fatal("pnil %P", p);
f1 = p->to.u.branch->opt;
if(f1 == nil)
fatal("fnil %P / %P", p, p->to.u.branch);
if(f1 == f) {
continue;
}
f->s2 = f1;
f->p2link = f1->p2;
f1->p2 = f;
}
}
graph->start = start;
graph->num = nf;
return graph;
}
void
flowend(Graph *graph)
{
Flow *f;
for(f = graph->start; f != nil; f = f->link)
f->prog->opt = nil;
free(graph);
}
static int32
postorder(Flow *r, Flow **rpo2r, int32 n)
{
Flow *r1;
r->rpo = 1;
r1 = r->s1;
if(r1 && !r1->rpo)
n = postorder(r1, rpo2r, n);
r1 = r->s2;
if(r1 && !r1->rpo)
n = postorder(r1, rpo2r, n);
rpo2r[n] = r;
n++;
return n;
}
static int32
rpolca(int32 *idom, int32 rpo1, int32 rpo2)
{
int32 t;
if(rpo1 == -1)
return rpo2;
while(rpo1 != rpo2){
if(rpo1 > rpo2){
t = rpo2;
rpo2 = rpo1;
rpo1 = t;
}
while(rpo1 < rpo2){
t = idom[rpo2];
if(t >= rpo2)
fatal("bad idom");
rpo2 = t;
}
}
return rpo1;
}
static int
doms(int32 *idom, int32 r, int32 s)
{
while(s > r)
s = idom[s];
return s == r;
}
static int
loophead(int32 *idom, Flow *r)
{
int32 src;
src = r->rpo;
if(r->p1 != nil && doms(idom, src, r->p1->rpo))
return 1;
for(r = r->p2; r != nil; r = r->p2link)
if(doms(idom, src, r->rpo))
return 1;
return 0;
}
static void
loopmark(Flow **rpo2r, int32 head, Flow *r)
{
if(r->rpo < head || r->active == head)
return;
r->active = head;
r->loop += LOOP;
if(r->p1 != nil)
loopmark(rpo2r, head, r->p1);
for(r = r->p2; r != nil; r = r->p2link)
loopmark(rpo2r, head, r);
}
void
flowrpo(Graph *g)
{
Flow *r1;
int32 i, d, me, nr, *idom;
Flow **rpo2r;
free(g->rpo);
g->rpo = calloc(g->num*sizeof g->rpo[0], 1);
idom = calloc(g->num*sizeof idom[0], 1);
if(g->rpo == nil || idom == nil)
fatal("out of memory");
for(r1 = g->start; r1 != nil; r1 = r1->link)
r1->active = 0;
rpo2r = g->rpo;
d = postorder(g->start, rpo2r, 0);
nr = g->num;
if(d > nr)
fatal("too many reg nodes %d %d", d, nr);
nr = d;
for(i = 0; i < nr / 2; i++) {
r1 = rpo2r[i];
rpo2r[i] = rpo2r[nr - 1 - i];
rpo2r[nr - 1 - i] = r1;
}
for(i = 0; i < nr; i++)
rpo2r[i]->rpo = i;
idom[0] = 0;
for(i = 0; i < nr; i++) {
r1 = rpo2r[i];
me = r1->rpo;
d = -1;
if(r1->p1 != nil && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me)
d = r1->p1->rpo;
for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
d = rpolca(idom, d, r1->rpo);
idom[i] = d;
}
for(i = 0; i < nr; i++) {
r1 = rpo2r[i];
r1->loop++;
if(r1->p2 != nil && loophead(idom, r1))
loopmark(rpo2r, i, r1);
}
free(idom);
for(r1 = g->start; r1 != nil; r1 = r1->link)
r1->active = 0;
}
Flow*
uniqp(Flow *r)
{
Flow *r1;
r1 = r->p1;
if(r1 == nil) {
r1 = r->p2;
if(r1 == nil || r1->p2link != nil)
return nil;
} else
if(r->p2 != nil)
return nil;
return r1;
}
Flow*
uniqs(Flow *r)
{
Flow *r1;
r1 = r->s1;
if(r1 == nil) {
r1 = r->s2;
if(r1 == nil)
return nil;
} else
if(r->s2 != nil)
return nil;
return r1;
}
typedef struct TempVar TempVar;
typedef struct TempFlow TempFlow;
struct TempVar
{
Node *node;
TempFlow *def;
TempFlow *use;
TempVar *freelink;
TempVar *merge;
vlong start;
vlong end;
uchar addr;
uchar removed;
};
struct TempFlow
{
Flow f;
TempFlow *uselink;
};
static int
startcmp(const void *va, const void *vb)
{
TempVar *a, *b;
a = *(TempVar**)va;
b = *(TempVar**)vb;
if(a->start < b->start)
return -1;
if(a->start > b->start)
return +1;
return 0;
}
static int
canmerge(Node *n)
{
return n->class == PAUTO && strncmp(n->sym->name, "autotmp", 7) == 0;
}
static void mergewalk(TempVar*, TempFlow*, uint32);
static void varkillwalk(TempVar*, TempFlow*, uint32);
void
mergetemp(Prog *firstp)
{
int i, j, nvar, ninuse, nfree, nkill;
TempVar *var, *v, *v1, **bystart, **inuse;
TempFlow *r;
NodeList *l, **lp;
Node *n;
Prog *p, *p1;
Type *t;
ProgInfo info, info1;
int32 gen;
Graph *g;
enum { Debug = 0 };
g = flowstart(firstp, sizeof(TempFlow));
if(g == nil)
return;
nvar = 0;
for(l = curfn->dcl; l != nil; l = l->next)
if(canmerge(l->n))
nvar++;
var = calloc(nvar*sizeof var[0], 1);
nvar = 0;
for(l = curfn->dcl; l != nil; l = l->next) {
n = l->n;
if(canmerge(n)) {
v = &var[nvar++];
n->opt = v;
v->node = n;
}
}
for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) {
p = r->f.prog;
proginfo(&info, p);
if(p->from.node != N && p->from.node->opt && p->to.node != N && p->to.node->opt)
fatal("double node %P", p);
if((n = p->from.node) != N && (v = n->opt) != nil ||
(n = p->to.node) != N && (v = n->opt) != nil) {
if(v->def == nil)
v->def = r;
r->uselink = v->use;
v->use = r;
if(n == p->from.node && (info.flags & LeftAddr))
v->addr = 1;
}
}
if(Debug > 1)
dumpit("before", g->start, 0);
nkill = 0;
for(v = var; v < var+nvar; v++) {
if(v->addr)
continue;
if((r = v->use) != nil && r->uselink == nil) {
p = r->f.prog;
proginfo(&info, p);
if(p->to.node == v->node && (info.flags & RightWrite) && !(info.flags & RightRead)) {
p->as = ANOP;
p->to = zprog.to;
v->removed = 1;
if(Debug)
print("drop write-only %S\n", v->node->sym);
} else
fatal("temp used and not set: %P", p);
nkill++;
continue;
}
if((r = v->use) != nil && r->f.link == &r->uselink->f && r->uselink->uselink == nil && uniqp(r->f.link) == &r->f) {
p = r->f.prog;
proginfo(&info, p);
p1 = r->f.link->prog;
proginfo(&info1, p1);
enum {
SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD,
};
if(p->from.node == v->node && p1->to.node == v->node && (info.flags & Move) &&
!((info.flags|info1.flags) & (LeftAddr|RightAddr)) &&
(info.flags & SizeAny) == (info1.flags & SizeAny)) {
p1->from = p->from;
excise(&r->f);
v->removed = 1;
if(Debug)
print("drop immediate-use %S\n", v->node->sym);
}
nkill++;
continue;
}
}
gen = 0;
for(v = var; v < var+nvar; v++) {
gen++;
for(r = v->use; r != nil; r = r->uselink)
mergewalk(v, r, gen);
if(v->addr) {
gen++;
for(r = v->use; r != nil; r = r->uselink)
varkillwalk(v, r, gen);
}
}
bystart = malloc(nvar*sizeof bystart[0]);
for(i=0; i<nvar; i++)
bystart[i] = &var[i];
qsort(bystart, nvar, sizeof bystart[0], startcmp);
inuse = malloc(nvar*sizeof inuse[0]);
ninuse = 0;
nfree = nvar;
for(i=0; i<nvar; i++) {
v = bystart[i];
if(v->removed)
continue;
while(ninuse > 0 && inuse[ninuse-1]->end < v->start) {
v1 = inuse[--ninuse];
inuse[--nfree] = v1;
}
t = v->node->type;
for(j=nfree; j<nvar; j++) {
v1 = inuse[j];
if(eqtype(t, v1->node->type) && v->node->addrtaken == v1->node->addrtaken) {
inuse[j] = inuse[nfree++];
if(v1->merge)
v->merge = v1->merge;
else
v->merge = v1;
nkill++;
break;
}
}
j = ninuse++;
while(j > 0 && inuse[j-1]->end < v->end) {
inuse[j] = inuse[j-1];
j--;
}
inuse[j] = v;
}
if(Debug) {
print("%S [%d - %d]\n", curfn->nname->sym, nvar, nkill);
for(v=var; v<var+nvar; v++) {
print("var %#N %T %lld-%lld", v->node, v->node->type, v->start, v->end);
if(v->addr)
print(" addr=1");
if(v->removed)
print(" dead=1");
if(v->merge)
print(" merge %#N", v->merge->node);
if(v->start == v->end)
print(" %P", v->def->f.prog);
print("\n");
}
if(Debug > 1)
dumpit("after", g->start, 0);
}
for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) {
p = r->f.prog;
if((n = p->from.node) != N && (v = n->opt) != nil && v->merge != nil)
p->from.node = v->merge->node;
if((n = p->to.node) != N && (v = n->opt) != nil && v->merge != nil)
p->to.node = v->merge->node;
}
for(lp = &curfn->dcl; (l = *lp); ) {
curfn->dcl->end = l;
n = l->n;
v = n->opt;
if(v && (v->merge || v->removed)) {
*lp = l->next;
continue;
}
lp = &l->next;
}
for(v=var; v<var+nvar; v++)
v->node->opt = nil;
free(var);
free(bystart);
free(inuse);
flowend(g);
}
static void
mergewalk(TempVar *v, TempFlow *r0, uint32 gen)
{
Prog *p;
TempFlow *r1, *r, *r2;
for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.p1) {
if(r1->f.active == gen)
break;
r1->f.active = gen;
p = r1->f.prog;
if(v->end < p->pc)
v->end = p->pc;
if(r1 == v->def) {
v->start = p->pc;
break;
}
}
for(r = r0; r != r1; r = (TempFlow*)r->f.p1)
for(r2 = (TempFlow*)r->f.p2; r2 != nil; r2 = (TempFlow*)r2->f.p2link)
mergewalk(v, r2, gen);
}
static void
varkillwalk(TempVar *v, TempFlow *r0, uint32 gen)
{
Prog *p;
TempFlow *r1, *r;
for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.s1) {
if(r1->f.active == gen)
break;
r1->f.active = gen;
p = r1->f.prog;
if(v->end < p->pc)
v->end = p->pc;
if(v->start > p->pc)
v->start = p->pc;
if(p->as == ARET || (p->as == AVARKILL && p->to.node == v->node))
break;
}
for(r = r0; r != r1; r = (TempFlow*)r->f.s1)
varkillwalk(v, (TempFlow*)r->f.s2, gen);
}
typedef struct NilVar NilVar;
typedef struct NilFlow NilFlow;
struct NilFlow {
Flow f;
int kill;
};
static void nilwalkback(NilFlow *rcheck);
static void nilwalkfwd(NilFlow *rcheck);
void
nilopt(Prog *firstp)
{
NilFlow *r;
Prog *p;
Graph *g;
int ncheck, nkill;
g = flowstart(firstp, sizeof(NilFlow));
if(g == nil)
return;
if(debug_checknil > 1 )
dumpit("nilopt", g->start, 0);
ncheck = 0;
nkill = 0;
for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) {
p = r->f.prog;
if(p->as != ACHECKNIL || !regtyp(&p->from))
continue;
ncheck++;
if(stackaddr(&p->from)) {
if(debug_checknil && p->lineno > 1)
warnl(p->lineno, "removed nil check of SP address");
r->kill = 1;
continue;
}
nilwalkfwd(r);
if(r->kill) {
if(debug_checknil && p->lineno > 1)
warnl(p->lineno, "removed nil check before indirect");
continue;
}
nilwalkback(r);
if(r->kill) {
if(debug_checknil && p->lineno > 1)
warnl(p->lineno, "removed repeated nil check");
continue;
}
}
for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) {
if(r->kill) {
nkill++;
excise(&r->f);
}
}
flowend(g);
if(debug_checknil > 1)
print("%S: removed %d of %d nil checks\n", curfn->nname->sym, nkill, ncheck);
}
static void
nilwalkback(NilFlow *rcheck)
{
Prog *p;
ProgInfo info;
NilFlow *r;
for(r = rcheck; r != nil; r = (NilFlow*)uniqp(&r->f)) {
p = r->f.prog;
proginfo(&info, p);
if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) {
return;
}
if(r != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from)) {
rcheck->kill = 1;
return;
}
}
}
static void
nilwalkfwd(NilFlow *rcheck)
{
NilFlow *r, *last;
Prog *p;
ProgInfo info;
last = nil;
for(r = (NilFlow*)uniqs(&rcheck->f); r != nil; r = (NilFlow*)uniqs(&r->f)) {
p = r->f.prog;
proginfo(&info, p);
if((info.flags & LeftRead) && smallindir(&p->from, &rcheck->f.prog->from)) {
rcheck->kill = 1;
return;
}
if((info.flags & (RightRead|RightWrite)) && smallindir(&p->to, &rcheck->f.prog->from)) {
rcheck->kill = 1;
return;
}
if(p->as == ACHECKNIL)
return;
if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from))
return;
if((info.flags & RightWrite) && !regtyp(&p->to))
return;
if(last != nil && r <= last)
return;
last = r;
}
}