#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]; voidspan(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; 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 = %#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 voidcheckpool(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 voidflushpool(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 */voidaddpool(Prog *p, Adr *a){ Prog *q, t; int c, sz; c = aclass(a); t = zprg; t.as = AWORD; sz = 4; if(p->as == AMOV) { t.as = ADWORD; sz = 8; } switch(c) { default: 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: t.to.type = D_CONST; t.to.offset = instoffset; sz = 4; 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;} intrelinv(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;} longregoff(Adr *a){ instoffset = 0; aclass(a); return instoffset;} static intispcdisp(long v){ /* pc-relative addressing will reach? */ return v >= -0xfffff && v <= 0xfffff && (v&3) == 0;} static intisaddcon(vlong v){ /* uimm12 or uimm24? */ if(v < 0) return 0; if((v & 0xFFF) == 0) v >>= 12; return v <= 0xFFF;} static intisbitcon(uvlong v){ /* fancy bimm32 or bimm64? */ return findmask(v) != nil || (v>>32) == 0 && findmask(v | (v<<32)) != nil;} static intmaxstr1(uvlong x){ int i; for(i = 0; x != 0; i++) x &= x<<1; return i;} static uvlongfindrotl(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 intfindmask64(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 intconstclass(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 */vlongoffsetshift(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<<s != v) diag("odd offset: %lld\n%P", v, curp); return vs;} /* * if v contains a single 16-bit value aligned * on a 16-bit field, and thus suitable for movk/movn, * return the field index 0 to 3; otherwise return -1 */intmovcon(vlong v){ int s; for(s = 0; s < 64; s += 16) if((v & ~((uvlong)0xFFFF<<s)) == 0) return s/16; return -1;} intaclass(Adr *a){ vlong v; Sym *s; int t; instoffset = 0; switch(a->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:Lines 1–500 of 1304. Load more
#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; 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 = %#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 c, sz; c = aclass(a); t = zprg; t.as = AWORD; sz = 4; if(p->as == AMOV) { t.as = ADWORD; sz = 8; } switch(c) { default: 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: t.to.type = D_CONST; t.to.offset = instoffset; sz = 4; 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; } 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 isbitcon(uvlong v) { /* fancy bimm32 or bimm64? */ return findmask(v) != nil || (v>>32) == 0 && findmask(v | (v<<32)) != nil; } 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<<s != v) diag("odd offset: %lld\n%P", v, curp); return vs; } /* * if v contains a single 16-bit value aligned * on a 16-bit field, and thus suitable for movk/movn, * return the field index 0 to 3; otherwise return -1 */ int movcon(vlong v) { int s; for(s = 0; s < 64; s += 16) if((v & ~((uvlong)0xFFFF<<s)) == 0) return s/16; return -1; } int aclass(Adr *a) { vlong v; Sym *s; int t; instoffset = 0; switch(a->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: