static void checkundef(Node *n, Reaching *r, Bitset *reach, Bitset *kill) { size_t i, j, did; Node *def; Type *t; if (n->type != Nexpr) return; if (exprop(n) == Ovar) { did = n->expr.did; for (j = 0; j < r->ndefs[did]; j++) { t = tybase(exprtype(n)); if (t->type == Tystruct || t->type == Tyunion || t->type == Tyarray || t->type == Tytuple) continue; if (bshas(kill, r->defs[did][j])) continue; if (!bshas(reach, r->defs[did][j])) continue; def = nodes[r->defs[did][j]]; if (exprop(def) == Oundef) fatal(n, "%s used before definition", namestr(n->expr.args[0])); } } else { switch (exprop(n)) { case Oset: case Oasn: case Oblit: checkundef(n->expr.args[1], r, reach, kill); break; case Oaddr: case Oslice: /* these don't actually look at the of args[0], so they're ok. */ for (i = 1; i < n->expr.nargs; i++) checkundef(n->expr.args[i], r, reach, kill); break; case Ocall: for (i = 1; i < n->expr.nargs; i++) if (exprop(n->expr.args[i]) != Oaddr) checkundef(n->expr.args[i], r, reach, kill); break; default: for (i = 0; i < n->expr.nargs; i++) checkundef(n->expr.args[i], r, reach, kill); break; } } }
static void bset(Ref r, Blk *b, int *nlv, Tmp *tmp) { if (rtype(r) != RTmp) return; bsset(b->gen, r.val); if (!bshas(b->in, r.val)) { nlv[KBASE(tmp[r.val].cls)]++; bsset(b->in, r.val); } }
int main() { Ins *i1; unsigned long long tm, rm, cnt; RMap mend; int reg[NIReg], val[NIReg+1]; int t, i, r, nr; tmp = (Tmp[Tmp0+NIReg]){{{0}}}; for (t=0; t<Tmp0+NIReg; t++) if (t >= Tmp0) { tmp[t].cls = Kw; tmp[t].hint.r = -1; tmp[t].hint.m = 0; tmp[t].slot = -1; sprintf(tmp[t].name, "tmp%d", t-Tmp0+1); } bsinit_(mbeg.b, Tmp0+NIReg); bsinit_(mend.b, Tmp0+NIReg); cnt = 0; for (tm = 0; tm < 1ull << (2*NIReg); tm++) { mbeg.n = 0; bszero(mbeg.b); ip = ins; /* find what temporaries are in copy and * wether or not they are in register */ for (t=0; t<NIReg; t++) switch ((tm >> (2*t)) & 3) { case 0: /* not in copy, not in reg */ break; case 1: /* not in copy, in reg */ radd(&mbeg, Tmp0+t, t+1); break; case 2: /* in copy, not in reg */ *ip++ = (Ins){OCopy, TMP(Tmp0+t), {R, R}, Kw}; break; case 3: /* in copy, in reg */ *ip++ = (Ins){OCopy, TMP(Tmp0+t), {R, R}, Kw}; radd(&mbeg, Tmp0+t, t+1); break; } if (ip == ins) /* cancel if the parallel move * is empty */ goto Nxt; /* find registers for temporaries * in mbeg */ nr = ip - ins; rm = (1ull << (nr+1)) - 1; for (i=0; i<nr; i++) reg[i] = i+1; for (;;) { /* set registers on copies */ for (i=0, i1=ins; i1<ip; i1++, i++) i1->arg[0] = TMP(reg[i]); /* compile the parallel move */ rcopy(&mend, &mbeg); dopm(&dummyb, ip-1, &mend); cnt++; /* check that mend contain mappings for * source registers and does not map any * assigned temporary, then check that * all temporaries in mend are mapped in * mbeg and not used in the copy */ for (i1=ins; i1<ip; i1++) { r = i1->arg[0].val; assert(rfree(&mend, r) == r); t = i1->to.val; assert(!bshas(mend.b, t)); } for (i=0; i<mend.n; i++) { t = mend.t[i]; assert(bshas(mbeg.b, t)); t -= Tmp0; assert(((tm >> (2*t)) & 3) == 1); } /* execute the code generated and check * that all assigned temporaries got their * value, and that all live variables's * content got preserved */ for (i=1; i<=NIReg; i++) val[i] = i; iexec(val); for (i1=ins; i1<ip; i1++) { t = i1->to.val; r = rfind(&mbeg, t); if (r != -1) assert(val[r] == i1->arg[0].val); } for (i=0; i<mend.n; i++) { t = mend.t[i]; r = mend.r[i]; assert(val[t-Tmp0+1] == r); } /* find the next register assignment */ i = nr - 1; for (;;) { r = reg[i]; rm &= ~(1ull<<r); do r++; while (r <= NIReg && (rm & (1ull<<r))); if (r == NIReg+1) { if (i == 0) goto Nxt; i--; } else { rm |= (1ull<<r); reg[i++] = r; break; } } for (; i<nr; i++) for (r=1; r<=NIReg; r++) if (!(rm & (1ull<<r))) { rm |= (1ull<<r); reg[i] = r; break; } } Nxt: freeall(); } printf("%llu tests successful!\n", cnt); exit(0); }
/* liveness analysis * requires rpo computation */ void filllive(Fn *f) { Blk *b; Ins *i; int k, t, m[2], n, chg, nlv[2]; BSet u[1], v[1]; Mem *ma; bsinit(u, f->ntmp); bsinit(v, f->ntmp); for (b=f->start; b; b=b->link) { bsinit(b->in, f->ntmp); bsinit(b->out, f->ntmp); bsinit(b->gen, f->ntmp); } chg = 1; Again: for (n=f->nblk-1; n>=0; n--) { b = f->rpo[n]; bscopy(u, b->out); if (b->s1) { liveon(v, b, b->s1); bsunion(b->out, v); } if (b->s2) { liveon(v, b, b->s2); bsunion(b->out, v); } chg |= !bsequal(b->out, u); memset(nlv, 0, sizeof nlv); b->out->t[0] |= T.rglob; bscopy(b->in, b->out); for (t=0; bsiter(b->in, &t); t++) nlv[KBASE(f->tmp[t].cls)]++; if (rtype(b->jmp.arg) == RCall) { assert((int)bscount(b->in) == T.nrglob && nlv[0] == T.nrglob && nlv[1] == 0); b->in->t[0] |= T.retregs(b->jmp.arg, nlv); } else bset(b->jmp.arg, b, nlv, f->tmp); for (k=0; k<2; k++) b->nlive[k] = nlv[k]; for (i=&b->ins[b->nins]; i!=b->ins;) { if ((--i)->op == Ocall && rtype(i->arg[1]) == RCall) { b->in->t[0] &= ~T.retregs(i->arg[1], m); for (k=0; k<2; k++) { nlv[k] -= m[k]; /* caller-save registers are used * by the callee, in that sense, * right in the middle of the call, * they are live: */ nlv[k] += T.nrsave[k]; if (nlv[k] > b->nlive[k]) b->nlive[k] = nlv[k]; } b->in->t[0] |= T.argregs(i->arg[1], m); for (k=0; k<2; k++) { nlv[k] -= T.nrsave[k]; nlv[k] += m[k]; } } if (!req(i->to, R)) { assert(rtype(i->to) == RTmp); t = i->to.val; if (bshas(b->in, i->to.val)) nlv[KBASE(f->tmp[t].cls)]--; bsset(b->gen, t); bsclr(b->in, t); } for (k=0; k<2; k++) switch (rtype(i->arg[k])) { case RMem: ma = &f->mem[i->arg[k].val]; bset(ma->base, b, nlv, f->tmp); bset(ma->index, b, nlv, f->tmp); break; default: bset(i->arg[k], b, nlv, f->tmp); break; } for (k=0; k<2; k++) if (nlv[k] > b->nlive[k]) b->nlive[k] = nlv[k]; } } if (chg) { chg = 0; goto Again; } if (debug['L']) { fprintf(stderr, "\n> Liveness analysis:\n"); for (b=f->start; b; b=b->link) { fprintf(stderr, "\t%-10sin: ", b->name); dumpts(b->in, f->tmp, stderr); fprintf(stderr, "\t out: "); dumpts(b->out, f->tmp, stderr); fprintf(stderr, "\t gen: "); dumpts(b->gen, f->tmp, stderr); fprintf(stderr, "\t live: "); fprintf(stderr, "%d %d\n", b->nlive[0], b->nlive[1]); } } }