ref: d388629dae7e6ab368015e3fc5c8c12c0e20fae1
dir: /sys/src/cmd/jl/span.c/
#include "l.h"
void
span(void)
{
Prog *p, *q;
Sym *setext, *s;
Optab *o;
int m, bflag, i, spass;
long c, otxt, v;
if(debug['v'])
Bprint(&bso, "%5.2f span\n", cputime());
Bflush(&bso);
bflag = 0;
c = 0;
otxt = c;
for(p = firstp; p != P; p = p->link) {
if(p->as == ATEXT)
c = (c + 3) & ~3;
p->pc = c;
o = oplook(p);
m = o->size;
if(!debug['c']){
if(o->ctype && asmout(p, o, 2) == 2){
bflag = 1;
p->mark |= COMPR;
m = 2;
}
}
if(m == 0) {
if(p->as == ATEXT) {
curtext = p;
autosize = p->to.offset + ptrsize;
if(p->from.sym != S)
p->from.sym->value = c;
/* need passes to resolve branches */
if(c-otxt >= 0x1000)
bflag = 1;
otxt = c;
continue;
}
diag("zero-width instruction\n%P", p);
continue;
}
c += m;
}
/*
* Multi-pass expansion of span dependent instructions
* Bcond JAL C.Bcond C.JAL C.JMP
*/
spass = 0;
while(bflag) {
if(debug['v'])
Bprint(&bso, "%5.2f span1\n", cputime());
bflag = 0;
spass ^= SPASS;
c = 0;
for(p = firstp; p != P; p = p->link) {
o = oplook(p);
m = o->size;
if(p->mark&COMPR)
m = 2;
/* opcode type beq or jal? */
if((o->type == 3 || o->type == 4) && p->cond) {
if((p->cond->mark&SPASS) == spass)
p->pc = c;
if(m == 2){
/*
* If instruction was compressed,
* check again in case
* branch range is now too large.
*/
m = asmout(p, o, 3);
if(m != 2){
p->mark &= ~COMPR;
bflag = 1;
}
}
otxt = p->cond->pc - p->pc;
if(otxt < 0)
otxt = -otxt;
if(o->type == 3){
/*
* If Bcond branch range exceeds 4K,
* replace it by the
* logically negated branch around a JMP.
*/
if(otxt >= 0x1000) {
q = prg();
q->link = p->link;
q->line = p->line;
q->as = AJMP;
q->to.type = D_BRANCH;
q->cond = p->cond;
p->link = q;
p->as = relinv(p->as);
p->cond = q->link;
p->optab = 0;
o = oplook(p);
q->mark = spass ^ SPASS;
m = asmout(p, o, 2);
if(m == 2)
p->mark |= COMPR;
q->pc = p->pc + m;
bflag = 1;
}
}else{
/*
* If JAL branch range exceeds 1M,
* change address class
* and recalculate instruction length.
*/
if(otxt >= 0x100000) {
p->to.class = C_LBRA + 1;
p->optab = 0;
o = oplook(p);
m = asmout(p, o, 3);
p->mark &= ~COMPR;
}
}
}
if(p->as == ATEXT)
c = (c + 3) & ~3;
p->pc = c;
p->mark ^= SPASS;
if(m == 0) {
if(p->as == ATEXT) {
curtext = p;
autosize = p->to.offset + ptrsize;
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;
}
if(INITRND)
INITDAT = rnd(INITTEXT + c, INITRND);
if(debug['v'])
Bprint(&bso, "tsize = %lux\n", textsize);
Bflush(&bso);
}
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;
a->class = aclass(a) + 1;
return instoffset;
}
int
classreg(Adr *a)
{
if(a->reg == NREG) {
switch(a->class - 1) {
case C_SEXT:
case C_SECON:
case C_LECON:
return REGSB;
case C_SAUTO:
case C_LAUTO:
case C_SACON:
case C_LACON:
return REGSP;
}
}
return a->reg;
}
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_CTLREG:
return C_CTLREG;
case D_FREG:
return C_FREG;
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;
}
t = a->sym->type;
if(t == 0 || t == SXREF) {
diag("undefined external: %s in %s",
a->sym->name, TNAME);
a->sym->type = SDATA;
}
instoffset = a->sym->value + a->offset - BIG;
if(instoffset >= -BIG && instoffset < BIG)
return C_SEXT;
return C_LEXT;
case D_AUTO:
instoffset = autosize + a->offset;
if(instoffset >= -BIG && instoffset < BIG)
return C_SAUTO;
return C_LAUTO;
case D_PARAM:
instoffset = autosize + a->offset + ptrsize;
if(instoffset >= -BIG && instoffset < BIG)
return C_SAUTO;
return C_LAUTO;
case D_NONE:
instoffset = a->offset;
if(instoffset == 0)
return C_ZOREG;
if(instoffset >= -BIG && instoffset < BIG)
return C_SOREG;
return C_LOREG;
}
return C_GOK;
case D_FCONST:
return C_FCON;
case D_VCONST:
return C_VCON;
case D_CONST:
switch(a->name) {
case D_NONE:
instoffset = a->offset;
if(a->reg != NREG && a->reg != REGZERO){
if(instoffset >= -BIG && instoffset < BIG)
return C_SRCON;
return C_LRCON;
}
consize:
if(instoffset == 0)
return C_ZCON;
if(instoffset >= -0x800 && instoffset <= 0x7ff)
return C_SCON;
if((instoffset & 0xfff) == 0)
return C_UCON;
return C_LCON;
case D_EXTERN:
case D_STATIC:
instoffx = 0;
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 SCONST:
instoffset = s->value + a->offset;
goto consize;
case STEXT:
case SLEAF:
case SSTRING:
instoffset = s->value + a->offset;
instoffx = INITTEXT;
return C_LECON;
}
instoffset = s->value + a->offset - BIG;
if(instoffset >= -BIG && instoffset < BIG && instoffset != 0L)
return C_SECON;
instoffset = s->value + a->offset;
instoffx = INITDAT;
return C_LECON;
case D_AUTO:
instoffset = autosize + a->offset;
if(instoffset >= -BIG && instoffset < BIG)
return C_SACON;
return C_LACON;
case D_PARAM:
instoffset = autosize + a->offset + ptrsize;
if(instoffset >= -BIG && instoffset < BIG)
return C_SACON;
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][a3];
if(a1) {
p->optab = a1+1;
return optab+a1;
}
o = oprange[r].stop; /* just generate an error */
a1 = p->from.class - 1;
}
e = oprange[r].stop;
c1 = xcmp[a1];
c3 = xcmp[a3];
for(; o<e; o++)
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);
if(!debug['a'])
prasm(p);
o = optab;
p->optab = (o-optab)+1;
return o;
}
int
cmp(int a, int b)
{
if(a == b)
return 1;
switch(a) {
case C_LCON:
if(b == C_ZCON || b == C_SCON || b == C_UCON)
return 1;
break;
case C_UCON:
if(b == C_ZCON)
return 1;
break;
case C_SCON:
if(b == C_ZCON)
return 1;
break;
case C_LACON:
if(b == C_SACON)
return 1;
break;
case C_LRCON:
if(b == C_SRCON)
return 1;
break;
case C_LBRA:
if(b == C_SBRA)
return 1;
break;
case C_LEXT:
if(b == C_SEXT)
return 1;
break;
case C_LAUTO:
if(b == C_SAUTO)
return 1;
break;
case C_ZREG:
if(b == C_REG || b == C_ZCON)
return 1;
break;
case C_LOREG:
if(b == C_ZOREG || b == C_SOREG || b == C_SAUTO || b == C_LAUTO)
return 1;
break;
case C_SOREG:
if(b == C_ZOREG || b == C_SAUTO || b == C_SEXT)
return 1;
break;
}
return 0;
}
int
ocmp(void *a1, void *a2)
{
Optab *p1, *p2;
int n;
p1 = (Optab*)a1;
p2 = (Optab*)a2;
n = p1->as - p2->as;
if(n)
return n;
n = p1->a1 - p2->a1;
if(n)
return n;
n = p1->a3 - p2->a3;
if(n)
return n;
return 0;
}
void
buildop(void)
{
int i, n, r;
for(i=0; i<32; i++)
for(n=0; n<32; n++)
xcmp[i][n] = cmp(n, i);
for(n=0; optab[n].as != AXXX; n++)
;
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--;
}
buildrep(1, AMOVW);
}
void
buildrep(int x, int as)
{
Opcross *p;
Optab *e, *s, *o;
int a1, a3, n;
if(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(a1=0; a1<32; a1++) {
if(!xcmp[a1][o->a1])
continue;
for(a3=0; a3<32; a3++)
if(xcmp[a3][o->a3])
(*p)[a1][a3] = n;
}
}
oprange[as].start = 0;
}