plan9fox/sys/src/cmd/5l/span.c
2011-03-30 19:35:09 +03:00

979 lines
16 KiB
C

#include "l.h"
static struct {
ulong start;
ulong size;
} pool;
void checkpool(Prog*);
void flushpool(Prog*, int);
void
span(void)
{
Prog *p;
Sym *setext, *s;
Optab *o;
int m, bflag, i;
long c, otxt, v;
if(debug['v'])
Bprint(&bso, "%5.2f span\n", cputime());
Bflush(&bso);
bflag = 0;
c = INITTEXT;
otxt = c;
for(p = firstp; p != P; p = p->link) {
p->pc = c;
o = oplook(p);
m = o->size;
if(m == 0) {
if(p->as == ATEXT) {
curtext = p;
autosize = p->to.offset + 4;
if(p->from.sym != S)
p->from.sym->value = c;
/* need passes to resolve branches */
if(c-otxt >= 1L<<17)
bflag = 1;
otxt = c;
continue;
}
diag("zero-width instruction\n%P", p);
continue;
}
switch(o->flag & (LFROM|LTO|LPOOL)) {
case LFROM:
addpool(p, &p->from);
break;
case LTO:
addpool(p, &p->to);
break;
case LPOOL:
if ((p->scond&C_SCOND) == 14)
flushpool(p, 0);
break;
}
if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14)
flushpool(p, 0);
c += m;
if(blitrl)
checkpool(p);
}
/*
* if any procedure is large enough to
* generate a large SBRA branch, then
* generate extra passes putting branches
* around jmps to fix. this is rare.
*/
while(bflag) {
if(debug['v'])
Bprint(&bso, "%5.2f span1\n", cputime());
bflag = 0;
c = INITTEXT;
for(p = firstp; p != P; p = p->link) {
p->pc = c;
o = oplook(p);
/* very larg branches
if(o->type == 6 && p->cond) {
otxt = p->cond->pc - c;
if(otxt < 0)
otxt = -otxt;
if(otxt >= (1L<<17) - 10) {
q = prg();
q->link = p->link;
p->link = q;
q->as = AB;
q->to.type = D_BRANCH;
q->cond = p->cond;
p->cond = q;
q = prg();
q->link = p->link;
p->link = q;
q->as = AB;
q->to.type = D_BRANCH;
q->cond = q->link->link;
bflag = 1;
}
}
*/
m = o->size;
if(m == 0) {
if(p->as == ATEXT) {
curtext = p;
autosize = p->to.offset + 4;
if(p->from.sym != S)
p->from.sym->value = c;
continue;
}
diag("zero-width instruction\n%P", p);
continue;
}
c += m;
}
}
if(debug['t']) {
/*
* add strings to text segment
*/
c = rnd(c, 8);
for(i=0; i<NHASH; i++)
for(s = hash[i]; s != S; s = s->link) {
if(s->type != SSTRING)
continue;
v = s->value;
while(v & 3)
v++;
s->value = c;
c += v;
}
}
c = rnd(c, 8);
setext = lookup("etext", 0);
if(setext != S) {
setext->value = c;
textsize = c - INITTEXT;
}
if(INITRND)
INITDAT = rnd(c, INITRND);
if(debug['v'])
Bprint(&bso, "tsize = %lux\n", textsize);
Bflush(&bso);
}
/*
* when the first reference to the literal pool threatens
* to go out of range of a 12-bit PC-relative offset,
* drop the pool now, and branch round it.
* this happens only in extended basic blocks that exceed 4k.
*/
void
checkpool(Prog *p)
{
if(pool.size >= 0xffc || immaddr((p->pc+4)+4+pool.size - pool.start+8) == 0)
flushpool(p, 1);
else if(p->link == P)
flushpool(p, 2);
}
void
flushpool(Prog *p, int skip)
{
Prog *q;
if(blitrl) {
if(skip){
if(debug['v'] && skip == 1)
print("note: flush literal pool at %lux: len=%lud ref=%lux\n", p->pc+4, pool.size, pool.start);
q = prg();
q->as = AB;
q->to.type = D_BRANCH;
q->cond = p->link;
q->link = blitrl;
blitrl = q;
}
else if(p->pc+pool.size-pool.start < 2048)
return;
elitrl->link = p->link;
p->link = blitrl;
blitrl = 0; /* BUG: should refer back to values until out-of-range */
elitrl = 0;
pool.size = 0;
pool.start = 0;
}
}
void
addpool(Prog *p, Adr *a)
{
Prog *q, t;
int c;
c = aclass(a);
t = zprg;
t.as = AWORD;
switch(c) {
default:
t.to = *a;
break;
case C_SROREG:
case C_LOREG:
case C_ROREG:
case C_FOREG:
case C_SOREG:
case C_FAUTO:
case C_SAUTO:
case C_LAUTO:
case C_LACON:
t.to.type = D_CONST;
t.to.offset = instoffset;
break;
}
for(q = blitrl; q != P; q = q->link) /* could hash on t.t0.offset */
if(memcmp(&q->to, &t.to, sizeof(t.to)) == 0) {
p->cond = q;
return;
}
q = prg();
*q = t;
q->pc = pool.size;
if(blitrl == P) {
blitrl = q;
pool.start = p->pc;
} else
elitrl->link = q;
elitrl = q;
pool.size += 4;
p->cond = q;
}
void
xdefine(char *p, int t, long v)
{
Sym *s;
s = lookup(p, 0);
if(s->type == 0 || s->type == SXREF) {
s->type = t;
s->value = v;
}
}
long
regoff(Adr *a)
{
instoffset = 0;
aclass(a);
return instoffset;
}
long
immrot(ulong v)
{
int i;
for(i=0; i<16; i++) {
if((v & ~0xff) == 0)
return (i<<8) | v | (1<<25);
v = (v<<2) | (v>>30);
}
return 0;
}
long
immaddr(long v)
{
if(v >= 0 && v <= 0xfff)
return (v & 0xfff) |
(1<<24) | /* pre indexing */
(1<<23); /* pre indexing, up */
if(v >= -0xfff && v < 0)
return (-v & 0xfff) |
(1<<24); /* pre indexing */
return 0;
}
int
immfloat(long v)
{
return (v & 0xC03) == 0; /* offset will fit in floating-point load/store */
}
int
immhalf(long v)
{
if(v >= 0 && v <= 0xff)
return v|
(1<<24)| /* pre indexing */
(1<<23); /* pre indexing, up */
if(v >= -0xff && v < 0)
return (-v & 0xff)|
(1<<24); /* pre indexing */
return 0;
}
int
aclass(Adr *a)
{
Sym *s;
int t;
switch(a->type) {
case D_NONE:
return C_NONE;
case D_REG:
return C_REG;
case D_REGREG:
return C_REGREG;
case D_SHIFT:
return C_SHIFT;
case D_FREG:
return C_FREG;
case D_FPCR:
return C_FCR;
case D_OREG:
switch(a->name) {
case D_EXTERN:
case D_STATIC:
if(a->sym == 0 || a->sym->name == 0) {
print("null sym external\n");
print("%D\n", a);
return C_GOK;
}
s = a->sym;
t = s->type;
if(t == 0 || t == SXREF) {
diag("undefined external: %s in %s",
s->name, TNAME);
s->type = SDATA;
}
if(dlm) {
switch(t) {
default:
instoffset = s->value + a->offset + INITDAT;
break;
case SUNDEF:
case STEXT:
case SCONST:
case SLEAF:
case SSTRING:
instoffset = s->value + a->offset;
break;
}
return C_ADDR;
}
instoffset = s->value + a->offset - BIG;
t = immaddr(instoffset);
if(t) {
if(immhalf(instoffset))
return immfloat(t) ? C_HFEXT : C_HEXT;
if(immfloat(t))
return C_FEXT;
return C_SEXT;
}
return C_LEXT;
case D_AUTO:
instoffset = autosize + a->offset;
t = immaddr(instoffset);
if(t){
if(immhalf(instoffset))
return immfloat(t) ? C_HFAUTO : C_HAUTO;
if(immfloat(t))
return C_FAUTO;
return C_SAUTO;
}
return C_LAUTO;
case D_PARAM:
instoffset = autosize + a->offset + 4L;
t = immaddr(instoffset);
if(t){
if(immhalf(instoffset))
return immfloat(t) ? C_HFAUTO : C_HAUTO;
if(immfloat(t))
return C_FAUTO;
return C_SAUTO;
}
return C_LAUTO;
case D_NONE:
instoffset = a->offset;
t = immaddr(instoffset);
if(t) {
if(immhalf(instoffset)) /* n.b. that it will also satisfy immrot */
return immfloat(t) ? C_HFOREG : C_HOREG;
if(immfloat(t))
return C_FOREG; /* n.b. that it will also satisfy immrot */
t = immrot(instoffset);
if(t)
return C_SROREG;
if(immhalf(instoffset))
return C_HOREG;
return C_SOREG;
}
t = immrot(instoffset);
if(t)
return C_ROREG;
return C_LOREG;
}
return C_GOK;
case D_PSR:
return C_PSR;
case D_OCONST:
switch(a->name) {
case D_EXTERN:
case D_STATIC:
s = a->sym;
t = s->type;
if(t == 0 || t == SXREF) {
diag("undefined external: %s in %s",
s->name, TNAME);
s->type = SDATA;
}
instoffset = s->value + a->offset + INITDAT;
if(s->type == STEXT || s->type == SLEAF || s->type == SUNDEF)
instoffset = s->value + a->offset;
return C_LCON;
}
return C_GOK;
case D_FCONST:
return C_FCON;
case D_CONST:
switch(a->name) {
case D_NONE:
instoffset = a->offset;
if(a->reg != NREG)
goto aconsize;
t = immrot(instoffset);
if(t)
return C_RCON;
t = immrot(~instoffset);
if(t)
return C_NCON;
return C_LCON;
case D_EXTERN:
case D_STATIC:
s = a->sym;
if(s == S)
break;
t = s->type;
switch(t) {
case 0:
case SXREF:
diag("undefined external: %s in %s",
s->name, TNAME);
s->type = SDATA;
break;
case SUNDEF:
case STEXT:
case SSTRING:
case SCONST:
case SLEAF:
instoffset = s->value + a->offset;
return C_LCON;
}
if(!dlm) {
instoffset = s->value + a->offset - BIG;
t = immrot(instoffset);
if(t && instoffset != 0)
return C_RECON;
}
instoffset = s->value + a->offset + INITDAT;
return C_LCON;
case D_AUTO:
instoffset = autosize + a->offset;
goto aconsize;
case D_PARAM:
instoffset = autosize + a->offset + 4L;
aconsize:
t = immrot(instoffset);
if(t)
return C_RACON;
return C_LACON;
}
return C_GOK;
case D_BRANCH:
return C_SBRA;
}
return C_GOK;
}
Optab*
oplook(Prog *p)
{
int a1, a2, a3, r;
char *c1, *c3;
Optab *o, *e;
a1 = p->optab;
if(a1)
return optab+(a1-1);
a1 = p->from.class;
if(a1 == 0) {
a1 = aclass(&p->from) + 1;
p->from.class = a1;
}
a1--;
a3 = p->to.class;
if(a3 == 0) {
a3 = aclass(&p->to) + 1;
p->to.class = a3;
}
a3--;
a2 = C_NONE;
if(p->reg != NREG)
a2 = C_REG;
r = p->as;
o = oprange[r].start;
if(o == 0) {
a1 = opcross[repop[r]][a1][a2][a3];
if(a1) {
p->optab = a1+1;
return optab+a1;
}
o = oprange[r].stop; /* just generate an error */
}
if(0) {
print("oplook %A %d %d %d\n",
(int)p->as, a1, a2, a3);
print(" %d %d\n", p->from.type, p->to.type);
}
e = oprange[r].stop;
c1 = xcmp[a1];
c3 = xcmp[a3];
for(; o<e; o++)
if(o->a2 == a2)
if(c1[o->a1])
if(c3[o->a3]) {
p->optab = (o-optab)+1;
return o;
}
diag("illegal combination %A %d %d %d",
p->as, a1, a2, a3);
prasm(p);
if(o == 0)
o = optab;
return o;
}
int
cmp(int a, int b)
{
if(a == b)
return 1;
switch(a) {
case C_LCON:
if(b == C_RCON || b == C_NCON)
return 1;
break;
case C_LACON:
if(b == C_RACON)
return 1;
break;
case C_LECON:
if(b == C_RECON)
return 1;
break;
case C_HFEXT:
return b == C_HEXT || b == C_FEXT;
case C_FEXT:
case C_HEXT:
return b == C_HFEXT;
case C_SEXT:
return cmp(C_HFEXT, b);
case C_LEXT:
return cmp(C_SEXT, b);
case C_HFAUTO:
return b == C_HAUTO || b == C_FAUTO;
case C_FAUTO:
case C_HAUTO:
return b == C_HFAUTO;
case C_SAUTO:
return cmp(C_HFAUTO, b);
case C_LAUTO:
return cmp(C_SAUTO, b);
case C_HFOREG:
return b == C_HOREG || b == C_FOREG;
case C_FOREG:
case C_HOREG:
return b == C_HFOREG;
case C_SROREG:
return cmp(C_SOREG, b) || cmp(C_ROREG, b);
case C_SOREG:
case C_ROREG:
return b == C_SROREG || cmp(C_HFOREG, b);
case C_LOREG:
return cmp(C_SROREG, b);
case C_LBRA:
if(b == C_SBRA)
return 1;
break;
}
return 0;
}
int
ocmp(const void *a1, const void *a2)
{
Optab *p1, *p2;
int n;
p1 = (Optab*)a1;
p2 = (Optab*)a2;
n = p1->as - p2->as;
if(n)
return n;
n = (p2->flag&V4) - (p1->flag&V4); /* architecture version */
if(n)
return n;
n = p1->a1 - p2->a1;
if(n)
return n;
n = p1->a2 - p2->a2;
if(n)
return n;
n = p1->a3 - p2->a3;
if(n)
return n;
return 0;
}
void
buildop(void)
{
int i, n, r;
armv4 = !debug['h'];
for(i=0; i<C_GOK; i++)
for(n=0; n<C_GOK; n++)
xcmp[i][n] = cmp(n, i);
for(n=0; optab[n].as != AXXX; n++)
if((optab[n].flag & V4) && !armv4) {
optab[n].as = AXXX;
break;
}
qsort(optab, n, sizeof(optab[0]), ocmp);
for(i=0; i<n; i++) {
r = optab[i].as;
oprange[r].start = optab+i;
while(optab[i].as == r)
i++;
oprange[r].stop = optab+i;
i--;
switch(r)
{
default:
diag("unknown op in build: %A", r);
errorexit();
case AADD:
oprange[AAND] = oprange[r];
oprange[AEOR] = oprange[r];
oprange[ASUB] = oprange[r];
oprange[ARSB] = oprange[r];
oprange[AADC] = oprange[r];
oprange[ASBC] = oprange[r];
oprange[ARSC] = oprange[r];
oprange[AORR] = oprange[r];
oprange[ABIC] = oprange[r];
break;
case ACMP:
oprange[ATST] = oprange[r];
oprange[ATEQ] = oprange[r];
oprange[ACMN] = oprange[r];
break;
case AMVN:
break;
case ABEQ:
oprange[ABNE] = oprange[r];
oprange[ABCS] = oprange[r];
oprange[ABHS] = oprange[r];
oprange[ABCC] = oprange[r];
oprange[ABLO] = oprange[r];
oprange[ABMI] = oprange[r];
oprange[ABPL] = oprange[r];
oprange[ABVS] = oprange[r];
oprange[ABVC] = oprange[r];
oprange[ABHI] = oprange[r];
oprange[ABLS] = oprange[r];
oprange[ABGE] = oprange[r];
oprange[ABLT] = oprange[r];
oprange[ABGT] = oprange[r];
oprange[ABLE] = oprange[r];
break;
case ASLL:
oprange[ASRL] = oprange[r];
oprange[ASRA] = oprange[r];
break;
case AMUL:
oprange[AMULU] = oprange[r];
break;
case ADIV:
oprange[AMOD] = oprange[r];
oprange[AMODU] = oprange[r];
oprange[ADIVU] = oprange[r];
break;
case AMOVW:
case AMOVB:
case AMOVBU:
case AMOVH:
case AMOVHU:
break;
case ASWPW:
oprange[ASWPBU] = oprange[r];
break;
case AB:
case ABL:
case ABX:
case ABXRET:
case ASWI:
case AWORD:
case AMOVM:
case ARFE:
case ATEXT:
case ACASE:
case ABCASE:
break;
case AADDF:
oprange[AADDD] = oprange[r];
oprange[ASUBF] = oprange[r];
oprange[ASUBD] = oprange[r];
oprange[AMULF] = oprange[r];
oprange[AMULD] = oprange[r];
oprange[ADIVF] = oprange[r];
oprange[ADIVD] = oprange[r];
oprange[AMOVFD] = oprange[r];
oprange[AMOVDF] = oprange[r];
break;
case ACMPF:
oprange[ACMPD] = oprange[r];
break;
case AMOVF:
oprange[AMOVD] = oprange[r];
break;
case AMOVFW:
oprange[AMOVWF] = oprange[r];
oprange[AMOVWD] = oprange[r];
oprange[AMOVDW] = oprange[r];
break;
case AMULL:
oprange[AMULA] = oprange[r];
oprange[AMULAL] = oprange[r];
oprange[AMULLU] = oprange[r];
oprange[AMULALU] = oprange[r];
break;
}
}
}
/*
void
buildrep(int x, int as)
{
Opcross *p;
Optab *e, *s, *o;
int a1, a2, a3, n;
if(C_NONE != 0 || C_REG != 1 || C_GOK >= 32 || x >= nelem(opcross)) {
diag("assumptions fail in buildrep");
errorexit();
}
repop[as] = x;
p = (opcross + x);
s = oprange[as].start;
e = oprange[as].stop;
for(o=e-1; o>=s; o--) {
n = o-optab;
for(a2=0; a2<2; a2++) {
if(a2) {
if(o->a2 == C_NONE)
continue;
} else
if(o->a2 != C_NONE)
continue;
for(a1=0; a1<32; a1++) {
if(!xcmp[a1][o->a1])
continue;
for(a3=0; a3<32; a3++)
if(xcmp[a3][o->a3])
(*p)[a1][a2][a3] = n;
}
}
}
oprange[as].start = 0;
}
*/
enum{
ABSD = 0,
ABSU = 1,
RELD = 2,
RELU = 3,
};
int modemap[4] = { 0, 1, -1, 2, };
typedef struct Reloc Reloc;
struct Reloc
{
int n;
int t;
uchar *m;
ulong *a;
};
Reloc rels;
static void
grow(Reloc *r)
{
int t;
uchar *m, *nm;
ulong *a, *na;
t = r->t;
r->t += 64;
m = r->m;
a = r->a;
r->m = nm = malloc(r->t*sizeof(uchar));
r->a = na = malloc(r->t*sizeof(ulong));
memmove(nm, m, t*sizeof(uchar));
memmove(na, a, t*sizeof(ulong));
free(m);
free(a);
}
void
dynreloc(Sym *s, long v, int abs)
{
int i, k, n;
uchar *m;
ulong *a;
Reloc *r;
if(v&3)
diag("bad relocation address");
v >>= 2;
if(s != S && s->type == SUNDEF)
k = abs ? ABSU : RELU;
else
k = abs ? ABSD : RELD;
/* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, a, a, k); */
k = modemap[k];
r = &rels;
n = r->n;
if(n >= r->t)
grow(r);
m = r->m;
a = r->a;
for(i = n; i > 0; i--){
if(v < a[i-1]){ /* happens occasionally for data */
m[i] = m[i-1];
a[i] = a[i-1];
}
else
break;
}
m[i] = k;
a[i] = v;
r->n++;
}
static int
sput(char *s)
{
char *p;
p = s;
while(*s)
cput(*s++);
cput(0);
return s-p+1;
}
void
asmdyn()
{
int i, n, t, c;
Sym *s;
ulong la, ra, *a;
vlong off;
uchar *m;
Reloc *r;
cflush();
off = seek(cout, 0, 1);
lput(0);
t = 0;
lput(imports);
t += 4;
for(i = 0; i < NHASH; i++)
for(s = hash[i]; s != S; s = s->link)
if(s->type == SUNDEF){
lput(s->sig);
t += 4;
t += sput(s->name);
}
la = 0;
r = &rels;
n = r->n;
m = r->m;
a = r->a;
lput(n);
t += 4;
for(i = 0; i < n; i++){
ra = *a-la;
if(*a < la)
diag("bad relocation order");
if(ra < 256)
c = 0;
else if(ra < 65536)
c = 1;
else
c = 2;
cput((c<<6)|*m++);
t++;
if(c == 0){
cput(ra);
t++;
}
else if(c == 1){
wput(ra);
t += 2;
}
else{
lput(ra);
t += 4;
}
la = *a++;
}
cflush();
seek(cout, off, 0);
lput(t);
if(debug['v']){
Bprint(&bso, "import table entries = %d\n", imports);
Bprint(&bso, "export table entries = %d\n", exports);
}
}