#include "l.h" #define BIT(n) ((uvlong)1<<(n)) static struct { ulong start; ulong size; } pool; static void checkpool(Prog*, int); static void flushpool(Prog*, int); static int ispcdisp(long); static Optab *badop; static Oprang oprange[ALAST]; 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) { if(p->as == ADWORD && (c&7) != 0) c += 4; p->pc = c; o = oplook(p); m = o->size; if(m == 0) { if(p->as == ATEXT) { curtext = p; autosize = p->to.offset + PCSZ; if(p->from.sym != S) p->from.sym->value = c; /* need passes to resolve branches */ if(c-otxt >= 1L<<20) bflag = 1; otxt = c; continue; } diag("zero-width instruction\n%P", p); continue; } switch(o->flag & (LFROM|LTO)) { case LFROM: addpool(p, &p->from); break; case LTO: addpool(p, &p->to); break; } if(p->as == AB || p->as == ARET || p->as == AERET || p->as == ARETURN) /* TO DO: other unconditional operations */ checkpool(p, 0); c += m; if(blitrl) checkpool(p, 1); } /* * 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) { if(p->as == ADWORD && (c&7) != 0) c += 4; p->pc = c; o = oplook(p); /* very large 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 + PCSZ; 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; ilink) { 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 = %#llux\n", textsize); Bflush(&bso); } /* * when the first reference to the literal pool threatens * to go out of range of a 1Mb PC-relative offset * drop the pool now, and branch round it. */ static void checkpool(Prog *p, int skip) { if(pool.size >= 0xffff0 || !ispcdisp(p->pc+4+pool.size - pool.start+8)) flushpool(p, skip); else if(p->link == P) flushpool(p, 2); } static void flushpool(Prog *p, int skip) { Prog *q; if(blitrl) { if(skip){ if(debug['v'] && skip == 1) print("note: flush literal pool at %#llux: 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 < 1024*1024) 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; } } /* * TO DO: hash */ void addpool(Prog *p, Adr *a) { Prog *q, t; int sz; t = zprg; t.as = AWORD; sz = 4; switch(aclass(a)) { default: if(p->as == AMOV && (a->name == D_EXTERN || a->name == D_STATIC) || (a->offset >> 32) != 0 && (a->offset >> 31) != -1){ t.as = ADWORD; sz = 8; } t.to = *a; break; case C_PSAUTO: case C_PPAUTO: case C_UAUTO4K: case C_UAUTO8K: case C_UAUTO16K: case C_UAUTO32K: case C_UAUTO64K: case C_NSAUTO: case C_NPAUTO: case C_LAUTO: case C_PPOREG: case C_PSOREG: case C_UOREG4K: case C_UOREG8K: case C_UOREG16K: case C_UOREG32K: case C_UOREG64K: case C_NSOREG: case C_NPOREG: case C_LOREG: case C_LACON: if((instoffset >> 32) != 0 && (instoffset >> 31) != -1) diag("offset too large\n%P", p); 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 = rnd(pool.size, sz); pool.size += sz; p->cond = q; } int relinv(int a) { switch(a) { case ABEQ: return ABNE; case ABNE: return ABEQ; case ABCS: return ABCC; case ABHS: return ABLO; case ABCC: return ABCS; case ABLO: return ABHS; case ABMI: return ABPL; case ABPL: return ABMI; case ABVS: return ABVC; case ABVC: return ABVS; case ABHI: return ABLS; case ABLS: return ABHI; case ABGE: return ABLT; case ABLT: return ABGE; case ABGT: return ABLE; case ABLE: return ABGT; } diag("unknown relation: %A", a); return a; } 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; } static int ispcdisp(long v) { /* pc-relative addressing will reach? */ return v >= -0xfffff && v <= 0xfffff && (v&3) == 0; } static int isaddcon(vlong v) { /* uimm12 or uimm24? */ if(v < 0) return 0; if((v & 0xFFF) == 0) v >>= 12; return v <= 0xFFF; } static int isbitcon64(uvlong v) { return findmask(v) != nil; } static int isbitcon32(uvlong v) { return (v >> 32) == 0 && findmask(v | v<<32) != nil; } static int isbitcon(uvlong v) { Mask *m; if((v >> 32) != 0) return 0; m = findmask(v); if(m == nil) return 0; if(m->s >= 32) return 0; return 1; } static int maxstr1(uvlong x) { int i; for(i = 0; x != 0; i++) x &= x<<1; return i; } static uvlong findrotl(uvlong x, int *l) { int i; for(i = 0; (x&1) == 0 || (x&BIT(63)) != 0; i++) x = (x<<1) | ((x&BIT(63))!=0); *l = i; return x; } static int findmask64(Mask *m, uvlong v) { uvlong x, f, fm; int i, lr, l0, l1, e; if(v == 0 || v == ~(uvlong)0) return 0; x = findrotl(v, &lr); l1 = maxstr1(x); l0 = maxstr1(~x); e = l0+l1; if(e == 0 || l1 == 64 || l0 == 64 || 64%e != 0) return 0; if(e != 64){ f = BIT(l1)-1; fm = BIT(e)-1; if(e > 32 && x != f) return 0; for(i = 0; i < 64; i += e) if(((x>>i) & fm) != f) return 0; } print("%#llux %#llux 1:%d 0:%d r:%d\n", v, x, l1, l0, lr%e); m->v = v; m->s = l1; m->e = e; m->r = lr%e; return 1; } /* * internal class codes for different constant classes: * they partition the constant/offset range into disjoint ranges that * are somehow treated specially by one or more load/store instructions. */ static int autoclass[] = {C_PSAUTO, C_NSAUTO, C_NPAUTO, C_PSAUTO, C_PPAUTO, C_UAUTO4K, C_UAUTO8K, C_UAUTO16K, C_UAUTO32K, C_UAUTO64K, C_LAUTO}; static int oregclass[] = {C_ZOREG, C_NSOREG, C_NPOREG, C_PSOREG, C_PPOREG, C_UOREG4K, C_UOREG8K, C_UOREG16K, C_UOREG32K, C_UOREG64K, C_LOREG}; static int sextclass[] = {C_SEXT1, C_LEXT, C_LEXT, C_SEXT1, C_SEXT1, C_SEXT1, C_SEXT2, C_SEXT4, C_SEXT8, C_SEXT16, C_LEXT}; /* * return appropriate index into tables above */ static int constclass(vlong l) { if(l == 0) return 0; if(l < 0){ if(l >= -256) return 1; if(l >= -512 && (l&7) == 0) return 2; return 10; } if(l <= 255) return 3; if(l <= 504 && (l&7) == 0) return 4; if(l <= 4095) return 5; if(l <= 8190 && (l&1) == 0) return 6; if(l <= 16380 && (l&3) == 0) return 7; if(l <= 32760 && (l&7) == 0) return 8; if(l <= 65520 && (l&0xF) == 0) return 9; return 10; } /* * given an offset v and a class c (see above) * return the offset value to use in the instruction, * scaled if necessary */ vlong offsetshift(vlong v, int c) { vlong vs; int s; static int shifts[] = {0, 1, 2, 3, 4}; s = 0; if(c >= C_SEXT1 && c <= C_SEXT16) s = shifts[c-C_SEXT1]; else if(c >= C_UAUTO4K && c <= C_UAUTO64K) s = shifts[c-C_UAUTO4K]; else if(c >= C_UOREG4K && c <= C_UOREG64K) s = shifts[c-C_UOREG4K]; vs = v>>s; if(vs<type) { case D_NONE: return C_NONE; case D_REG: return C_REG; case D_VREG: return C_VREG; case D_SP: return C_RSP; case D_COND: return C_COND; case D_SHIFT: return C_SHIFT; case D_EXTREG: return C_EXTREG; case D_ROFF: return C_ROFF; case D_XPOST: return C_XPOST; case D_XPRE: return C_XPRE; 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; } 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; if(instoffset >= 0) return sextclass[constclass(instoffset)]; return C_LEXT; case D_AUTO: instoffset = autosize + a->offset; return autoclass[constclass(instoffset)]; case D_PARAM: instoffset = autosize + a->offset + PCSZ; return autoclass[constclass(instoffset)]; case D_NONE: instoffset = a->offset; return oregclass[constclass(instoffset)]; } return C_GOK; case D_SPR: return C_SPR; 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 && a->reg != REGZERO) goto aconsize; v = instoffset; if(v == 0) return C_ZCON; if(isaddcon(v)){ if(isbitcon(v)) return C_ABCON; if(v <= 0xFFF) return C_ADDCON0; return C_ADDCON; } t = movcon(v); if(t >= 0){ if(isbitcon(v)) return C_MBCON; return C_MOVCON; } t = movcon(~v); if(t >= 0){ if(isbitcon(v)) return C_MBCON; return C_MOVCON; } if(isbitcon(v)) return C_BITCON; if(isbitcon64(v)) return C_BITCON64; if(isbitcon32(v)) return C_BITCON32; 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; if(instoffset != 0 && isaddcon(instoffset)) return C_AECON; } 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 + PCSZ; aconsize: if(isaddcon(instoffset)) return C_AACON; 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, *c2, *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]; c2 = xcmp[a2]; c3 = xcmp[a3]; for(; oa2 == a2 || c2[o->a2]) if(c1[o->a1]) if(c3[o->a3]) { if(0) print("%P\t-> %d (%d %d %d)\n", p, o->type, o->a1, o->a2, o->a3); p->optab = (o-optab)+1; return o; } diag("illegal combination %A %R %R %R", p->as, a1, a2, a3); prasm(p); o = badop; if(o == 0) errorexit(); return o; } int cmp(int a, int b) { if(a == b) return 1; switch(a) { case C_RSP: if(b == C_REG) return 1; break; case C_REG: if(b == C_ZCON) return 1; break; case C_ADDCON0: if(b == C_ZCON) return 1; break; case C_ADDCON: if(b == C_ZCON || b == C_ADDCON0 || b == C_ABCON) return 1; break; case C_BITCON32: case C_BITCON64: if(b == C_BITCON) return 1; /* wet floor */ case C_BITCON: if(b == C_ABCON || b == C_MBCON) return 1; break; case C_MOVCON: if(b == C_MBCON || b == C_ZCON || b == C_ADDCON0) return 1; break; case C_LCON: if(b == C_ZCON || b == C_BITCON || b == C_BITCON32 || b == C_BITCON64 || b == C_ADDCON || b == C_ADDCON0 || b == C_ABCON || b == C_MBCON || b == C_MOVCON) return 1; break; case C_VCON: return cmp(C_LCON, b); case C_LACON: if(b == C_AACON) return 1; break; case C_SEXT2: if(b == C_SEXT1) return 1; break; case C_SEXT4: if(b == C_SEXT1 || b == C_SEXT2) return 1; break; case C_SEXT8: if(b >= C_SEXT1 && b <= C_SEXT4) return 1; break; case C_SEXT16: if(b >= C_SEXT1 && b <= C_SEXT8) return 1; break; case C_LEXT: if(b >= C_SEXT1 && b <= C_SEXT16) return 1; break; case C_PPAUTO: if(b == C_PSAUTO) return 1; break; case C_UAUTO4K: if(b == C_PSAUTO || b == C_PPAUTO) return 1; break; case C_UAUTO8K: return cmp(C_UAUTO4K, b); case C_UAUTO16K: return cmp(C_UAUTO8K, b); case C_UAUTO32K: return cmp(C_UAUTO16K, b); case C_UAUTO64K: return cmp(C_UAUTO32K, b); case C_NPAUTO: return cmp(C_NSAUTO, b); case C_LAUTO: return cmp(C_NPAUTO, b) || cmp(C_UAUTO64K, b); case C_PSOREG: if(b == C_ZOREG) return 1; break; case C_PPOREG: if(b == C_ZOREG || b == C_PSOREG) return 1; break; case C_UOREG4K: if(b == C_ZOREG || b == C_PSAUTO || b == C_PSOREG || b == C_PPAUTO || b == C_PPOREG) return 1; break; case C_UOREG8K: return cmp(C_UOREG4K, b); case C_UOREG16K: return cmp(C_UOREG8K, b); case C_UOREG32K: return cmp(C_UOREG16K, b); case C_UOREG64K: return cmp(C_UOREG32K, b); case C_NPOREG: return cmp(C_NSOREG, b); case C_LOREG: return cmp(C_NPOREG, b) || cmp(C_UOREG64K, b); case C_LBRA: if(b == C_SBRA) return 1; break; } return 0; } static 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 = 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; Oprang t; for(i=0; i