#include "l.h" static struct { ulong start; ulong size; ulong extra;} pool; int checkpool(Prog*, int);int flushpool(Prog*, int, int); intisbranch(Prog *p){ int as = p->as; return (as >= ABEQ && as <= ABLE) || as == AB || as == ABL || as == ABX;} static intispad(Prog *p){ if(p->as != AMOVW) return 0; if(p->from.type != D_REG || p->from.reg != REGSB) return 0; if(p->to.type != D_REG || p->to.reg != REGSB) return 0; return 1;} intfninc(Sym *s){ if(thumb){ if(s->thumb){ if(s->foreign) return 8; else return 0; } else{ if(s->foreign) return 0; else diag("T A !foreign in fninc"); } } else{ if(s->thumb){ if(s->foreign) return 0; else diag("A T !foreign in fninc"); } else{ if(s->foreign) return 4; else return 0; } } return 0;} int fnpinc(Sym *s){ if(!s->fnptr){ // a simplified case BX O(R) -> BL O(R) if(!debug['f']) diag("fnptr == 0 in fnpinc"); if(s->foreign) diag("bad usage in fnpinc %s %d %d %d", s->name, s->used, s->foreign, s->thumb); return 0; } /* 0, 1, 2, 3 squared */ if(s->thumb) return s->foreign ? 9 : 1; else return s->foreign ? 4 : 0;} static Prog *pad(Prog *p, int pc){ Prog *q; q = prg(); q->as = AMOVW; q->line = p->line; q->from.type = D_REG; q->from.reg = REGSB; q->to.type = D_REG; q->to.reg = REGSB; q->pc = pc; q->link = p->link; return q;} static intscan(Prog *op, Prog *p, int c){ Prog *q; for(q = op->link; q != p; q = q->link){ q->pc = c; c += oplook(q)->size; nocache(q); } return c;} /* size of a case statement including jump table */static longcasesz(Prog *p){ int jt = 0; long n = 0; Optab *o; for( ; p != P; p = p->link){ if(p->as == ABCASE) jt = 1; else if(jt) break; o = oplook(p); n += o->size; } return n;} voidspan(void){ Prog *p, *op; Sym *setext, *s; Optab *o; int m, bflag, i; long c, otxt, v; int lastthumb = -1; if(debug['v']) Bprint(&bso, "%5.2f span\n", cputime()); Bflush(&bso); bflag = 0; c = INITTEXT; op = nil; otxt = c; for(p = firstp; p != P; op = p, p = p->link) { setarch(p); p->pc = c; o = oplook(p); m = o->size; // must check literal pool here in case p generates many instructions if(blitrl){ if(thumb && isbranch(p)) pool.extra += brextra(p); if(checkpool(op, p->as == ACASE ? casesz(p) : m)) c = p->pc = scan(op, p, c); } if(m == 0) { if(p->as == ATEXT) { if(blitrl && lastthumb != -1 && lastthumb != thumb){ // flush literal pool if(flushpool(op, 0, 1)) c = p->pc = scan(op, p, c); } lastthumb = thumb; 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; if(thumb && blitrl) pool.extra += brextra(p); 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, 0); break; } if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14) flushpool(p, 0, 0); c += m; if(blitrl && p->link == P){ if(thumb && isbranch(p)) pool.extra += brextra(p); checkpool(p, 0); } } /* * 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) { setarch(p); p->pc = c; if(thumb && isbranch(p)) nocache(p); 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(seenthumb){ // branch resolution int passes = 0; int lastc = 0; int again; Prog *oop; loop: passes++; if(passes > 150){ diag("span looping !"); errorexit(); } c = INITTEXT; oop = op = nil; again = 0; for(p = firstp; p != P; oop = op, op = p, p = p->link){ setarch(p); if(p->pc != c) again = 1; p->pc = c; if(thumb && isbranch(p)) nocache(p); o = oplook(p); m = o->size; if(passes == 1 && thumb && isbranch(p)){ // start conservative so unneeded alignment is not added if(p->as == ABL) m = 4; else m = 2; p->align = 0; } if(p->align){ if((p->align == 4 && (c&3)) || (p->align == 2 && !(c&3))){ if(ispad(op)){ oop->link = p; op = oop; c -= 2; p->pc = c; } else{ op->link = pad(op, c); op = op->link; c += 2; p->pc = c; } again = 1; } } 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; } } c += m; } if(c != lastc || again){ lastc = c; goto loop; } } if(0 && seenthumb){ // rm redundant padding - obsolete int d; op = nil; d = 0; for(p = firstp; p != P; op = p, p = p->link){ p->pc -= d; if(p->as == ATEXT){ if(p->from.sym != S) p->from.sym->value -= d;// if(p->from.sym != S) print("%s %ux %d %d %d\n", p->from.sym->name ? p->from.sym->name : "?", p->from.sym->value, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr); } if(ispad(p) && p->link != P && ispad(p->link)){ op->link = p->link->link; d += 4; p = op; } } // print("%d bytes removed (padding)\n", d); c -= d; } 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. */intcheckpool(Prog *p, int sz){ if(thumb){ if(pool.size >= 0x3fc || (p->pc+sz+pool.extra+2+2)+(pool.size-4)-pool.start-4 >= 0x3fc) return flushpool(p, 1, 0); else if(p->link == P) return flushpool(p, 2, 0); return 0; } if(pool.size >= 0xffc || immaddr((p->pc+sz+4)+4+pool.size - pool.start+8) == 0) return flushpool(p, 1, 0); else if(p->link == P) return flushpool(p, 2, 0); return 0;} intflushpool(Prog *p, int skip, int force){ Prog *q; if(blitrl) { if(skip){ if(0 && 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(!force && (p->pc+pool.size-pool.start < (thumb ? 0x3fc+4-pool.extra : 2048))) return 0; 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; pool.extra = 0; return 1; } return 0;} voidaddpool(Prog *p, Adr *a){ Prog *q, t; int c; if(thumb) c = thumbaclass(a, p); else 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_HOREG: case C_GOREG: case C_FAUTO: case C_SAUTO: case C_LAUTO: case C_LACON: case C_GACON: 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; q->align = 4; } else elitrl->link = q; elitrl = q; pool.size += 4; p->cond = q;} voidxdefine(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; }} Lines 1–500 of 1262. Load more
#include "l.h" static struct { ulong start; ulong size; ulong extra; } pool; int checkpool(Prog*, int); int flushpool(Prog*, int, int); int isbranch(Prog *p) { int as = p->as; return (as >= ABEQ && as <= ABLE) || as == AB || as == ABL || as == ABX; } static int ispad(Prog *p) { if(p->as != AMOVW) return 0; if(p->from.type != D_REG || p->from.reg != REGSB) return 0; if(p->to.type != D_REG || p->to.reg != REGSB) return 0; return 1; } int fninc(Sym *s) { if(thumb){ if(s->thumb){ if(s->foreign) return 8; else return 0; } else{ if(s->foreign) return 0; else diag("T A !foreign in fninc"); } } else{ if(s->thumb){ if(s->foreign) return 0; else diag("A T !foreign in fninc"); } else{ if(s->foreign) return 4; else return 0; } } return 0; } int fnpinc(Sym *s) { if(!s->fnptr){ // a simplified case BX O(R) -> BL O(R) if(!debug['f']) diag("fnptr == 0 in fnpinc"); if(s->foreign) diag("bad usage in fnpinc %s %d %d %d", s->name, s->used, s->foreign, s->thumb); return 0; } /* 0, 1, 2, 3 squared */ if(s->thumb) return s->foreign ? 9 : 1; else return s->foreign ? 4 : 0; } static Prog * pad(Prog *p, int pc) { Prog *q; q = prg(); q->as = AMOVW; q->line = p->line; q->from.type = D_REG; q->from.reg = REGSB; q->to.type = D_REG; q->to.reg = REGSB; q->pc = pc; q->link = p->link; return q; } static int scan(Prog *op, Prog *p, int c) { Prog *q; for(q = op->link; q != p; q = q->link){ q->pc = c; c += oplook(q)->size; nocache(q); } return c; } /* size of a case statement including jump table */ static long casesz(Prog *p) { int jt = 0; long n = 0; Optab *o; for( ; p != P; p = p->link){ if(p->as == ABCASE) jt = 1; else if(jt) break; o = oplook(p); n += o->size; } return n; } void span(void) { Prog *p, *op; Sym *setext, *s; Optab *o; int m, bflag, i; long c, otxt, v; int lastthumb = -1; if(debug['v']) Bprint(&bso, "%5.2f span\n", cputime()); Bflush(&bso); bflag = 0; c = INITTEXT; op = nil; otxt = c; for(p = firstp; p != P; op = p, p = p->link) { setarch(p); p->pc = c; o = oplook(p); m = o->size; // must check literal pool here in case p generates many instructions if(blitrl){ if(thumb && isbranch(p)) pool.extra += brextra(p); if(checkpool(op, p->as == ACASE ? casesz(p) : m)) c = p->pc = scan(op, p, c); } if(m == 0) { if(p->as == ATEXT) { if(blitrl && lastthumb != -1 && lastthumb != thumb){ // flush literal pool if(flushpool(op, 0, 1)) c = p->pc = scan(op, p, c); } lastthumb = thumb; 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; if(thumb && blitrl) pool.extra += brextra(p); 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, 0); break; } if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == 14) flushpool(p, 0, 0); c += m; if(blitrl && p->link == P){ if(thumb && isbranch(p)) pool.extra += brextra(p); checkpool(p, 0); } } /* * 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) { setarch(p); p->pc = c; if(thumb && isbranch(p)) nocache(p); 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(seenthumb){ // branch resolution int passes = 0; int lastc = 0; int again; Prog *oop; loop: passes++; if(passes > 150){ diag("span looping !"); errorexit(); } c = INITTEXT; oop = op = nil; again = 0; for(p = firstp; p != P; oop = op, op = p, p = p->link){ setarch(p); if(p->pc != c) again = 1; p->pc = c; if(thumb && isbranch(p)) nocache(p); o = oplook(p); m = o->size; if(passes == 1 && thumb && isbranch(p)){ // start conservative so unneeded alignment is not added if(p->as == ABL) m = 4; else m = 2; p->align = 0; } if(p->align){ if((p->align == 4 && (c&3)) || (p->align == 2 && !(c&3))){ if(ispad(op)){ oop->link = p; op = oop; c -= 2; p->pc = c; } else{ op->link = pad(op, c); op = op->link; c += 2; p->pc = c; } again = 1; } } 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; } } c += m; } if(c != lastc || again){ lastc = c; goto loop; } } if(0 && seenthumb){ // rm redundant padding - obsolete int d; op = nil; d = 0; for(p = firstp; p != P; op = p, p = p->link){ p->pc -= d; if(p->as == ATEXT){ if(p->from.sym != S) p->from.sym->value -= d; // if(p->from.sym != S) print("%s %ux %d %d %d\n", p->from.sym->name ? p->from.sym->name : "?", p->from.sym->value, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr); } if(ispad(p) && p->link != P && ispad(p->link)){ op->link = p->link->link; d += 4; p = op; } } // print("%d bytes removed (padding)\n", d); c -= d; } 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. */ int checkpool(Prog *p, int sz) { if(thumb){ if(pool.size >= 0x3fc || (p->pc+sz+pool.extra+2+2)+(pool.size-4)-pool.start-4 >= 0x3fc) return flushpool(p, 1, 0); else if(p->link == P) return flushpool(p, 2, 0); return 0; } if(pool.size >= 0xffc || immaddr((p->pc+sz+4)+4+pool.size - pool.start+8) == 0) return flushpool(p, 1, 0); else if(p->link == P) return flushpool(p, 2, 0); return 0; } int flushpool(Prog *p, int skip, int force) { Prog *q; if(blitrl) { if(skip){ if(0 && 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(!force && (p->pc+pool.size-pool.start < (thumb ? 0x3fc+4-pool.extra : 2048))) return 0; 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; pool.extra = 0; return 1; } return 0; } void addpool(Prog *p, Adr *a) { Prog *q, t; int c; if(thumb) c = thumbaclass(a, p); else 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_HOREG: case C_GOREG: case C_FAUTO: case C_SAUTO: case C_LAUTO: case C_LACON: case C_GACON: 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; q->align = 4; } 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; } }