#include        "l.h"

int     nbrnoop;
int     nldnoop;
int     ncmnoop;
int     nmfnoop;
int     nbrtot;
int     nldtot;
int     ncmtot;
int     nmagex;
int     nmfrom;

void
noops(void)
{
       Prog *p, *q, *q1, *m;
       int f;

       /*
        * 1. find leaf subroutines
        * 2. strip NOPs
        * 3. mark instruction that might need delays
        * 4. expand RET pseudo
        */

       if(debug['v'])
               Bprint(&bso, "%5.2f noops\n", cputime());
       Bflush(&bso);
       nbrnoop = 0;
       nbrtot = 0;
       nldnoop = 0;
       nldtot = 0;
       ncmnoop = 0;
       ncmtot = 0;
       nmagex = 0;
       nmfrom = 0;
       nmfnoop = 0;

       q = P;
       for(p = firstp; p != P; p = p->link) {

               switch(p->as) {
               case ATEXT:
                       q = p;
                       p->mark |= LEAF;
                       if(p->link)
                               p->link->mark |= LABEL;
                       curtext = p;
                       continue;

               /* too hard, just leave alone */
               case AMOVW:
                       if(p->to.type != D_FCREG &&
                          p->to.type != D_MREG) {
                               q = p;
                               continue;
                       }

               /* too hard, just leave alone */
               case ASYSCALL:
               case AWORD:
               case ATLBWR:
               case ATLBWI:
               case ATLBP:
               case ATLBR:
                       q = p;
                       p->mark |= LABEL;
                       for(q1=p->link; q1 != P; q1 = q1->link) {
                               q1->mark |= LABEL;
                               if(q1->as != ANOR)
                                       break;
                       }
                       continue;

               case ARET:
                       q = p;
                       if(p->link != P)
                               p->link->mark |= LABEL;
                       continue;

               case ANOP:
                       q1 = p->link;
                       q->link = q1;           /* q is non-nop */
                       q1->mark |= p->mark;
                       continue;

               case ABGEZAL:
               case ABLTZAL:
               case AJAL:
                       if(curtext != P)
                               curtext->mark &= ~LEAF;
               case ABEQ:
               case ABGEZ:
               case ABGTZ:
               case ABLEZ:
               case ABLTZ:
               case ABNE:
               case ABFPT:
               case ABFPF:
               case AJMP:
                       q = p;
                       q1 = p->cond;
                       if(q1 != P) {
                               while(q1->as == ANOP) {
                                       q1 = q1->link;
                                       p->cond = q1;
                               }
                               if(!(q1->mark & LEAF))
                                       q1->mark |= LABEL;
                       } else
                               p->mark |= LABEL;
                       q1 = p->link;
                       if(q1 != P)
                               q1->mark |= LABEL;
                       continue;

               default:
                       q = p;
                       continue;
               }
       }

       m = P;
       f = 0;
       for(q = firstp; q != P; q = p->link) {
               if(q->mark & LABEL) {
                       if(f)
                               sched(m, p);
                       m = q;
                       f = 0;
               }
               p = q;
       loop:
               switch(p->as) {
               case ATEXT:
                       curtext = p;
                       autosize = p->to.offset + 4;
                       if(autosize <= 4)
                       if(curtext->mark & LEAF) {
                               p->to.offset = -4;
                               autosize = 0;
                       }

                       q = p;
                       if(autosize) {
                               q = prg();
                               q->as = AADD;
                               q->line = p->line;
                               q->from.type = D_CONST;
                               q->from.offset = -autosize;
                               q->to.type = D_REG;
                               q->to.reg = REGSP;

                               q->link = p->link;
                               p->link = q;
                       } else
                       if(!(curtext->mark & LEAF)) {
                               if(debug['v'])
                                       Bprint(&bso, "save suppressed in: %s\n",
                                               curtext->from.sym->name);
                               Bflush(&bso);
                               curtext->mark |= LEAF;
                       }

                       if(curtext->mark & LEAF) {
                               if(curtext->from.sym)
                                       curtext->from.sym->type = SLEAF;
                               break;
                       }

                       q1 = prg();
                       q1->as = AMOVW;
                       q1->line = p->line;
                       q1->from.type = D_REG;
                       q1->from.reg = REGLINK;
                       q1->to.type = D_OREG;
                       q1->from.offset = 0;
                       q1->to.reg = REGSP;

                       q1->link = q->link;
                       q->link = q1;
                       break;

               case ARET:
                       nocache(p);
                       if(curtext->mark & LEAF) {
                               if(!autosize) {
                                       p->as = AJMP;
                                       p->from = zprg.from;
                                       p->to.type = D_OREG;
                                       p->to.offset = 0;
                                       p->to.reg = REGLINK;
                                       goto loop;
                               }

                               p->as = AADD;
                               p->from.type = D_CONST;
                               p->from.offset = autosize;
                               p->to.type = D_REG;
                               p->to.reg = REGSP;

                               q = prg();
                               q->as = AJMP;
                               q->line = p->line;
                               q->to.type = D_OREG;
                               q->to.offset = 0;
                               q->to.reg = REGLINK;

                               q->link = p->link;
                               p->link = q;
                               goto loop;
                       }
                       p->as = AMOVW;
                       p->from.type = D_OREG;
                       p->from.offset = 0;
                       p->from.reg = REGSP;
                       p->to.type = D_REG;
                       p->to.reg = 2;

                       q = p;
                       if(autosize) {
                               q = prg();
                               q->as = AADD;
                               q->line = p->line;
                               q->from.type = D_CONST;
                               q->from.offset = autosize;
                               q->to.type = D_REG;
                               q->to.reg = REGSP;

                               q->link = p->link;
                               p->link = q;
                       }

                       q1 = prg();
                       q1->as = AJMP;
                       q1->line = p->line;
                       q1->to.type = D_OREG;
                       q1->to.offset = 0;
                       q1->to.reg = 2;

                       q1->link = q->link;
                       q->link = q1;
                       goto loop;

               case ACMPEQF:
               case ACMPEQD:
               case ACMPGTF:
               case ACMPGTD:
               case ACMPGEF:
               case ACMPGED:
                       f++;
                       p->mark |= COMPARE;
                       break;

               case AMOVB:
               case AMOVBU:
               case AMOVH:
               case AMOVHU:
               case AMOVW:
               case AMOVF:
               case AMOVD:
                       if(p->from.type == D_HI || p->from.type == D_LO) {
                               p->mark |= MFROM;
                               f++;
                               break;
                       }
                       if(p->from.type == D_OREG)
                               goto tst;
                       if(p->to.type == D_FREG) {
                               if(p->from.type == D_FCONST)
                                       goto tst;
                               if(p->from.type == D_CONST)
                                       goto tst;
                               if(p->from.type == D_REG)
                                       goto tst;
                       }
                       if(p->to.type == D_REG) {
                               if(p->from.type == D_FREG)
                                       goto tst;
                               if(p->from.type == D_FCREG)
                                       goto tst;
                               if(p->from.type == D_MREG)
                                       goto tst;
                       }
                       break;
               tst:
                       f++;
                       p->mark |= LOAD;
                       break;

               case ABEQ:
               case ABGEZ:
               case ABGTZ:
               case ABLEZ:
               case ABLTZ:
               case ABNE:
               case ABFPT:
               case ABFPF:
               case AJMP:
               case ABGEZAL:
               case ABLTZAL:
               case AJAL:
                       p->mark |= BRANCH;
                       sched(m, p);
                       f = 0;
                       break;
               }
       }
       if(debug['v'])
       Bprint(&bso, "br=%d/%d; ld=%d/%d; cm=%d/%d; mf=%d/%d; ex=%d\n",
               nbrnoop, nbrtot,
               nldnoop, nldtot,
               ncmnoop, ncmtot,
               nmfnoop, nmfrom,
               nmagex);
       Bflush(&bso);
}

void
nocache(Prog *p)
{
       p->optab = 0;
       p->from.class = 0;
       p->to.class = 0;
}

void
sched(Prog *p0, Prog *p1)
{
       Prog *p, *q, *t;
       int r, a;

       if(debug['X'])
               Bprint(&bso, "\n");
       for(p=p0;; p=p->link) {
               /*
                * magic optimization #1
                * add $c1,r1; sgt $c2,r1,r2
                *      is converted to
                * sgt $c2-c1,r1,r2; add $c1,r1
                */
               if(p->as == AADDU)
               if(p != p1)
               if(p->from.type == D_CONST)
               if(p->from.offset >= 0)
               if(p->to.type == D_REG) {
                       q = p->link;
                       a = q->as;
                       r = p->to.reg;
                       if(p->reg == r || p->reg == NREG)
                       if(a == ASGT)
                       if(q->from.type == D_CONST)
                       if(q->from.offset >= 0)
                       if(q->from.offset-p->from.offset >= 0)
                       if(q->reg == r)
                       if(q->to.type == D_REG)
                       if(q->to.reg != r) {
                               nocache(q);
                               q->from.offset -= p->from.offset;
                               nmagex++;
                               exchange(p);
                       }
               }
               /*
                * magic optimization #2
                * sub r1,r2,r3; bne r3,place
                *      is converted to
                * sub r1,r2,r3; bne r1,r2,place
                */
               if(p->as == ASUBU)
               if(p != p1)
               if(p->from.type == D_REG)
               if(p->to.type == D_REG)
               if(p->to.reg != p->from.reg)
               if(p->to.reg != p->reg)
               if(p->reg != NREG) {
                       q = p->link;
                       if(q->as == ABNE || q->as == ABEQ)
                       if(q->from.type == D_REG)
                       if(q->from.reg == p->to.reg)
                       if(q->reg == NREG) {
                               nocache(q);
                               q->from.reg = p->from.reg;
                               q->reg = p->reg;
                               nmagex++;
                       }
               }

               p->regused = regused(p);
               p->mark |= ACTIVE1|ACTIVE2;
               if(compound(p))
                       p->mark &= ~(ACTIVE1|ACTIVE2);
               if(debug['X'])
                       Bprint(&bso, "%2x       %.8lux%P\n", p->mark, p->regused, p);
               for(q=p0; q!=p; q=q->link)
                       if(depend(q->regused, p->regused))
                               q->mark &= ~ACTIVE1;
               if(p->mark & BRANCH) {
                       nbrtot++;
                       if(!debug['C'])
                       for(q=p0; q!=p; q=q->link) {
                               if(q->mark & (BRANCH|LOAD|COMPARE))
                                       continue;
                               if(!(q->mark & ACTIVE1))
                                       continue;
                               if(debug['X'])
                                       Bprint(&bso, "\t\t\t!!!%P\n", q);
                               append(q, p);
                               goto cont1;
                       }
                       addnop(p);
                       nbrnoop++;
                       goto cont1;
               }
       cont1:
               if(p == p1)
                       break;
       }

       for(p=p0;; p=p->link) {
               for(q=p0; q!=p; q=q->link)
                       if(depend(q->regused, p->regused))
                               q->mark &= ~ACTIVE2;
               if(p->mark & MFROM) {
                       nmfrom++;
                       p->mark &= ~ACTIVE2;
                       if(p == p1) {
                               addnop(p);
                               addnop(p);
                               nmfnoop += 2;
                               goto cont;
                       }

                       q = p->link;
                       switch(q->as) {
                       case AMOVW:
                               if(q->to.type != p->from.type)
                                       break;
                       case AMUL:
                       case AMULU:
                       case ADIV:
                       case ADIVU:
                               addnop(p);
                               addnop(p);
                               nmfnoop += 2;
                               goto cont;
                       }
                       q->mark &= ~ACTIVE2;

                       if(q == p1) {
                               addnop(p);
                               nmfnoop++;
                               goto cont;
                       }
                       q = q->link;
                       switch(q->as) {
                       case AMOVW:
                               if(q->to.type != p->from.type)
                                       break;
                       case AMUL:
                       case AMULU:
                       case ADIV:
                       case ADIVU:
                               addnop(p);
                               nmfnoop++;
                               goto cont;
                       }
                       q->mark &= ~ACTIVE2;
               }
               if(p->mark & BRANCH)
                       break;
               if(p->mark & COMPARE) { /* FCMP to BFPF */
                       ncmtot++;
                       p->mark &= ~ACTIVE2;
                       if(p == p1) {
                               ncmnoop++;
                               goto nop;
                       }
                       q = p->link;
                       if(q->as == ABFPF || q->as == ABFPT) {
                               ncmnoop++;
                               goto nop;
                       }
                       q->mark &= ~ACTIVE2;
                       goto cont;
               }
               if(p->mark & LOAD) {    /* mem load to use */
                       p->mark &= ~ACTIVE2;
                       nldtot++;
                       if(debug['C']) {
                               q = p->link;
                               if(p == p1 || conflict(p->regused, q->regused)) {
                                       nldnoop++;
                                       goto nop;
                               }
                               q->mark &= ~ACTIVE2;
                               goto cont;
                       }
                       for(q=p0; q!=p; q=q->link) {
                               if(q->mark & (BRANCH|LOAD|COMPARE))
                                       continue;
                               if(!(q->mark & ACTIVE2))
                                       continue;
                               if(debug['Y']) {
                                       Bprint(&bso, "%P !!!\n", q);
                                       for(t=q->link; t!=p; t=t->link)
                                               Bprint(&bso, "%P\n", t);
                                       Bprint(&bso, "%P\n\n", p);
                               }
                               q->mark &= ~ACTIVE2;
                               append(q, p);
                               goto cont;
                       }
                       for(q=p->link; q!=p1->link; q=q->link) {
                               if(q->mark & BRANCH) {
                                       if(conflict(p->regused, q->regused))
                                               break;
                                       if(q != p->link)
                                               break;
                                       q->mark &= ~ACTIVE2;
                                       goto cont;
                               }
                               if(q != p->link && !(q->mark & ACTIVE2))
                                       continue;
                               if(conflict(p->regused, q->regused))
                                       continue;
                               for(t=p->link; t!=q; t=t->link)
                                       if(depend(t->regused, q->regused))
                                               goto brk;
                               t = p->link;
                               if(t != q)
                                       prepend(t, q);
                               t->mark &= ~ACTIVE2;
                               goto cont;
                       brk:;
                       }
                       nldnoop++;
                       goto nop;
               }
       cont:
               if(p == p1)
                       break;
               continue;
       nop:
               addnop(p);
               if(p == p1)
                       break;
               p = p->link;
       }
}

void
addnop(Prog *p)
{
       Prog *q;

       q = prg();
       q->as = ANOR;
       q->line = p->line;
       q->from.type = D_REG;
       q->from.reg = REGZERO;
       q->to.type = D_REG;
       q->to.reg = REGZERO;

       q->link = p->link;
       p->link = q;
}

#define E_REG   (0)
#define E_FREG  (E_REG+32)
#define E_MEM   (E_FREG+32)
#define E_HILO  (E_MEM+1)       /* cpu hi+lo registers */

int
depend(long a, long b)
{
       int ra, rb;

       ra = a & 0xff;
       rb = b & 0xff;

       if(ra) {
               if(rb) {
                       if(ra == rb)
                               return 1;
                       if(ra == ((b>>8)&0xff))
                               return 1;
                       if(rb == ((a>>8)&0xff))
                               return 1;
                       if(ra == ((b>>16)&0xff))
                               return 1;
                       if(rb == ((a>>16)&0xff))
                               return 1;
                       if(ra == ((b>>24)&0xff))
                               return 1;
                       if(rb == ((a>>24)&0xff))
                               return 1;
                       return 0;
               }
               if(ra == ((b>>8)&0xff))
                       return 1;
               if(ra == ((b>>16)&0xff))
                       return 1;
               if(ra == ((b>>24)&0xff))
                       return 1;
               return 0;
       }
       if(rb) {
               if(rb == ((a>>8)&0xff))
                       return 1;
               if(rb == ((a>>16)&0xff))
                       return 1;
               if(rb == ((a>>24)&0xff))
                       return 1;
               return 0;
       }
       return 0;
}

int
conflict(long a, long b)
{

       a &= 0xff;
       if(a) {
               if(a == (b&0xff))
                       return 1;
               if(a == ((b>>8)&0xff))
                       return 1;
               if(a == ((b>>16)&0xff))
                       return 1;
               if(a == ((b>>24)&0xff))
                       return 1;
       }
       return 0;
}

int
compound(Prog *p)
{
       Optab *o;

       o = oplook(p);
       if(o->size != 4)
               return 1;
       if(p->to.type == D_REG && p->to.reg == REGSB)
               return 1;
       return 0;
}

void
prepend(Prog *p, Prog *q)
{

       if(p != q) {
               prepend(p->link, q);
               exchange(p);
       }
}

void
append(Prog *p, Prog *q)
{
       int m;

       m = p->mark & (LABEL|LEAF);
       if(m) {
               p->mark &= m;
               p->link->mark |= m;
       }
       for(;;) {
               exchange(p);
               p = p->link;
               if(p == q)
                       break;
       }
}

void
exchange(Prog *p)
{
       Prog g, *q;

       q = p->link;
       g = *p;
       *p = *q;
       *q = g;
       q->link = p->link;
       p->link = q;
}

long
regused(Prog *p)
{
       long r;
       int c, a;

       r = 0;
       a = p->as;
       switch(a) {
       case AMUL:
       case AMULU:
       case ADIV:
       case ADIVU:
               r |= E_HILO << 0;
               break;
       }
       c = p->to.class;
       if(c == 0) {
               c = aclass(&p->to) + 1;
               p->to.class = c;
       }
       c--;
       switch(c) {
       default:
               print("aclass to %d%P\n", c, p);
               break;
       case C_NONE:
       case C_ZCON:
       case C_SCON:
       case C_ADD0CON:
       case C_AND0CON:
       case C_ADDCON:
       case C_ANDCON:
       case C_UCON:
       case C_LCON:
       case C_MREG:
       case C_FCREG:
               break;
       case C_SBRA:
       case C_LBRA:
               if(a == AJAL)
                       r |= (E_REG+REGLINK) << 0;
               break;
       case C_REG:
               r |= (E_REG+p->to.reg) << 0;
               break;
       case C_FREG:
               r |= (E_FREG+p->to.reg) << 0;
               break;
       case C_HI:
       case C_LO:
               r |= E_HILO << 0;
               break;
       case C_SACON:
       case C_LACON:
               r |= (E_REG+REGSP) << 8;
               break;
       case C_SAUTO:
       case C_LAUTO:
               r |= (E_MEM << 0) | ((E_REG+REGSP) << 8);
               break;
       case C_SECON:
       case C_LECON:
               r |= (E_REG+REGSB) << 8;
               break;
       case C_SEXT:
       case C_LEXT:
               r |= (E_MEM << 0) | ((E_REG+REGSB) << 8);
               break;
       case C_ZOREG:
       case C_SOREG:
       case C_LOREG:
               if(p->as == AJMP)
                       r |= (E_REG+p->to.reg) << 8;
               else
               if(p->as == AJAL)
                       r |= ((E_REG+REGLINK) << 0) | ((E_REG+p->to.reg) << 8);
               else
                       r |= (E_MEM << 0) | ((E_REG+p->to.reg) << 8);
               break;
       }

       c = p->from.class;
       if(c == 0) {
               c = aclass(&p->from) + 1;
               p->from.class = c;
       }
       c--;
       switch(c) {
       default:
               print("aclass fr %d%P\n", c, p);
       case C_NONE:
       case C_ZCON:
       case C_SCON:
       case C_ADD0CON:
       case C_AND0CON:
       case C_ADDCON:
       case C_ANDCON:
       case C_UCON:
       case C_LCON:
       case C_SBRA:
       case C_LBRA:
       case C_MREG:
       case C_FCREG:
               break;
       case C_REG:
               r |= (E_REG+p->from.reg) << 16;
               break;
       case C_FREG:
               r |= (E_FREG+p->from.reg) << 16;
               break;
       case C_HI:
       case C_LO:
               r |= E_HILO << 16;
               break;
       case C_SACON:
       case C_LACON:
               r |= (E_REG+REGSP) << 16;
               break;
       case C_SAUTO:
       case C_LAUTO:
               r |= (E_MEM << 24) | ((E_REG+REGSP) << 16);
               break;
       case C_SECON:
       case C_LECON:
               r |= (E_REG+REGSB) << 16;
               break;
       case C_SEXT:
       case C_LEXT:
               r |= (E_MEM << 24) | ((E_REG+REGSB) << 16);
               break;
       case C_ZOREG:
       case C_SOREG:
       case C_LOREG:
               r |= (E_MEM << 24) | ((E_REG+p->from.reg) << 16);
               break;
       }
       c = p->reg;
       if(c != NREG) {
               c += E_REG;
               if(p->from.type == D_FREG || p->to.type == D_FREG)
                       c += E_FREG-E_REG;
               if(!(r & (0xff<<8)))
                       r |= c << 8;
               else
               if(!(r & (0xff<<16)))
                       r |= c << 16;
               else
               if(!(r & (0xff<<24)))
                       r |= c << 24;
               else
                       print("no room %P %x\n", p, r);
       }
       return r;
}