#include "gc.h"

void
peep(void)
{
       Reg *r, *r1, *r2;
       Prog *p, *p1;
       int t;
/*
* complete R structure
*/
       t = 0;
       for(r=firstr; r!=R; r=r1) {
               r1 = r->link;
               if(r1 == R)
                       break;
               p = r->prog->link;
               while(p != r1->prog)
               switch(p->as) {
               default:
                       r2 = rega();
                       r->link = r2;
                       r2->link = r1;

                       r2->prog = p;
                       r2->p1 = r;
                       r->s1 = r2;
                       r2->s1 = r1;
                       r1->p1 = r2;

                       r = r2;
                       t++;

               case ADATA:
               case AGLOBL:
               case ANAME:
               case ASIGNAME:
                       p = p->link;
               }
       }

loop1:
       t = 0;
       for(r=firstr; r!=R; r=r->link) {
               p = r->prog;
               if(p->as == AMOVW || p->as == AFMOVS || p->as == AFMOVD)
               if(regtyp(&p->to)) {
                       if(regtyp(&p->from))
                       if(p->from.type == p->to.type) {
                               if(copyprop(r)) {
                                       excise(r);
                                       t++;
                               } else
                               if(subprop(r) && copyprop(r)) {
                                       excise(r);
                                       t++;
                               }
                       }
                       if(regzer(&p->from))
                       if(p->to.type == D_REG) {
                               p->from.type = D_REG;
                               p->from.reg = REGZERO;
                               if(copyprop(r)) {
                                       excise(r);
                                       t++;
                               } else
                               if(subprop(r) && copyprop(r)) {
                                       excise(r);
                                       t++;
                               }
                       }
               }
       }
       if(t)
               goto loop1;
       /*
        * look for MOVB x,R; MOVB R,R
        */
       for(r=firstr; r!=R; r=r->link) {
               p = r->prog;
               switch(p->as) {
               default:
                       continue;
               case AMOVH:
               case AMOVHZ:
               case AMOVB:
               case AMOVBZ:
                       if(p->to.type != D_REG)
                               continue;
                       break;
               }
               r1 = r->link;
               if(r1 == R)
                       continue;
               p1 = r1->prog;
               if(p1->as != p->as)
                       continue;
               if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
                       continue;
               if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
                       continue;
               excise(r1);
       }

       if(debug['Q'] > 1)
               return; /* allow following code improvement to be suppressed */

       /*
        * look for OP x,y,R; CMP R, $0 -> OPCC x,y,R
        * when OP can set condition codes correctly
        */
       for(r=firstr; r!=R; r=r->link) {
               p = r->prog;
               switch(p->as) {
               case ACMP:
                       if(!regzer(&p->to))
                               continue;
                       r1 = r->s1;
                       if(r1 == R)
                               continue;
                       switch(r1->prog->as) {
                       default:
                               continue;
                       case ABCL:
                       case ABC:
                               /* the conditions can be complex and these are currently little used */
                               continue;
                       case ABEQ:
                       case ABGE:
                       case ABGT:
                       case ABLE:
                       case ABLT:
                       case ABNE:
                       case ABVC:
                       case ABVS:
                               break;
                       }
                       r1 = r;
                       do
                               r1 = uniqp(r1);
                       while (r1 != R && r1->prog->as == ANOP);
                       if(r1 == R)
                               continue;
                       p1 = r1->prog;
                       if(p1->to.type != D_REG || p1->to.reg != p->from.reg)
                               continue;
                       switch(p1->as) {
                       case ASUB:
                       case AADD:
                       case AXOR:
                       case AOR:
                               /* irregular instructions */
                               if(p1->from.type == D_CONST)
                                       continue;
                               break;
                       }
                       switch(p1->as) {
                       default:
                               continue;
                       case AMOVW:
                               if(p1->from.type != D_REG)
                                       continue;
                               continue;
                       case AANDCC:
                       case AANDNCC:
                       case AORCC:
                       case AORNCC:
                       case AXORCC:
                       case ASUBCC:
                       case ASUBECC:
                       case ASUBMECC:
                       case ASUBZECC:
                       case AADDCC:
                       case AADDCCC:
                       case AADDECC:
                       case AADDMECC:
                       case AADDZECC:
                       case ARLWMICC:
                       case ARLWNMCC:
                               t = p1->as;
                               break;
                       /* don't deal with floating point instructions for now */
/*
                       case AFABS:     t = AFABSCC; break;
                       case AFADD:     t = AFADDCC; break;
                       case AFADDS:    t = AFADDSCC; break;
                       case AFCTIW:    t = AFCTIWCC; break;
                       case AFCTIWZ:   t = AFCTIWZCC; break;
                       case AFDIV:     t = AFDIVCC; break;
                       case AFDIVS:    t = AFDIVSCC; break;
                       case AFMADD:    t = AFMADDCC; break;
                       case AFMADDS:   t = AFMADDSCC; break;
                       case AFMOVD:    t = AFMOVDCC; break;
                       case AFMSUB:    t = AFMSUBCC; break;
                       case AFMSUBS:   t = AFMSUBSCC; break;
                       case AFMUL:     t = AFMULCC; break;
                       case AFMULS:    t = AFMULSCC; break;
                       case AFNABS:    t = AFNABSCC; break;
                       case AFNEG:     t = AFNEGCC; break;
                       case AFNMADD:   t = AFNMADDCC; break;
                       case AFNMADDS:  t = AFNMADDSCC; break;
                       case AFNMSUB:   t = AFNMSUBCC; break;
                       case AFNMSUBS:  t = AFNMSUBSCC; break;
                       case AFRSP:     t = AFRSPCC; break;
                       case AFSUB:     t = AFSUBCC; break;
                       case AFSUBS:    t = AFSUBSCC; break;
                       case ACNTLZW:   t = ACNTLZWCC; break;
                       case AMTFSB0:   t = AMTFSB0CC; break;
                       case AMTFSB1:   t = AMTFSB1CC; break;
*/
                       case AADD:      t = AADDCC; break;
                       case AADDV:     t = AADDVCC; break;
                       case AADDC:     t = AADDCCC; break;
                       case AADDCV:    t = AADDCVCC; break;
                       case AADDME:    t = AADDMECC; break;
                       case AADDMEV:   t = AADDMEVCC; break;
                       case AADDE:     t = AADDECC; break;
                       case AADDEV:    t = AADDEVCC; break;
                       case AADDZE:    t = AADDZECC; break;
                       case AADDZEV:   t = AADDZEVCC; break;
                       case AAND:      t = AANDCC; break;
                       case AANDN:     t = AANDNCC; break;
                       case ADIVW:     t = ADIVWCC; break;
                       case ADIVWV:    t = ADIVWVCC; break;
                       case ADIVWU:    t = ADIVWUCC; break;
                       case ADIVWUV:   t = ADIVWUVCC; break;
                       case AEQV:      t = AEQVCC; break;
                       case AEXTSB:    t = AEXTSBCC; break;
                       case AEXTSH:    t = AEXTSHCC; break;
                       case AMULHW:    t = AMULHWCC; break;
                       case AMULHWU:   t = AMULHWUCC; break;
                       case AMULLW:    t = AMULLWCC; break;
                       case AMULLWV:   t = AMULLWVCC; break;
                       case ANAND:     t = ANANDCC; break;
                       case ANEG:      t = ANEGCC; break;
                       case ANEGV:     t = ANEGVCC; break;
                       case ANOR:      t = ANORCC; break;
                       case AOR:       t = AORCC; break;
                       case AORN:      t = AORNCC; break;
                       case AREM:      t = AREMCC; break;
                       case AREMV:     t = AREMVCC; break;
                       case AREMU:     t = AREMUCC; break;
                       case AREMUV:    t = AREMUVCC; break;
                       case ARLWMI:    t = ARLWMICC; break;
                       case ARLWNM:    t = ARLWNMCC; break;
                       case ASLW:      t = ASLWCC; break;
                       case ASRAW:     t = ASRAWCC; break;
                       case ASRW:      t = ASRWCC; break;
                       case ASUB:      t = ASUBCC; break;
                       case ASUBV:     t = ASUBVCC; break;
                       case ASUBC:     t = ASUBCCC; break;
                       case ASUBCV:    t = ASUBCVCC; break;
                       case ASUBME:    t = ASUBMECC; break;
                       case ASUBMEV:   t = ASUBMEVCC; break;
                       case ASUBE:     t = ASUBECC; break;
                       case ASUBEV:    t = ASUBEVCC; break;
                       case ASUBZE:    t = ASUBZECC; break;
                       case ASUBZEV:   t = ASUBZEVCC; break;
                       case AXOR:      t = AXORCC; break;
                               break;
                       }
                       if(debug['Q'])
                               print("cmp %P; %P -> ", p1, p);
                       p1->as = t;
                       if(debug['Q'])
                               print("%P\n", p1);
                       excise(r);
                       continue;
               }
       }
}

void
excise(Reg *r)
{
       Prog *p;

       p = r->prog;
       p->as = ANOP;
       p->from = zprog.from;
       p->from3 = zprog.from3;
       p->to = zprog.to;
       p->reg = zprog.reg; /**/
}

Reg*
uniqp(Reg *r)
{
       Reg *r1;

       r1 = r->p1;
       if(r1 == R) {
               r1 = r->p2;
               if(r1 == R || r1->p2link != R)
                       return R;
       } else
               if(r->p2 != R)
                       return R;
       return r1;
}

Reg*
uniqs(Reg *r)
{
       Reg *r1;

       r1 = r->s1;
       if(r1 == R) {
               r1 = r->s2;
               if(r1 == R)
                       return R;
       } else
               if(r->s2 != R)
                       return R;
       return r1;
}

/*
* if the system forces R0 to be zero,
* convert references to $0 to references to R0.
*/
regzer(Adr *a)
{
       if(R0ISZERO) {
               if(a->type == D_CONST)
                       if(a->sym == S)
                               if(a->offset == 0)
                                       return 1;
               if(a->type == D_REG)
                       if(a->reg == REGZERO)
                               return 1;
       }
       return 0;
}

regtyp(Adr *a)
{

       if(a->type == D_REG) {
               if(!R0ISZERO || a->reg != REGZERO)
                       return 1;
               return 0;
       }
       if(a->type == D_FREG)
               return 1;
       return 0;
}

/*
* the idea is to substitute
* one register for another
* from one MOV to another
*      MOV     a, R0
*      ADD     b, R0   / no use of R1
*      MOV     R0, R1
* would be converted to
*      MOV     a, R1
*      ADD     b, R1
*      MOV     R1, R0
* hopefully, then the former or latter MOV
* will be eliminated by copy propagation.
*/
int
subprop(Reg *r0)
{
       Prog *p;
       Adr *v1, *v2;
       Reg *r;
       int t;

       p = r0->prog;
       v1 = &p->from;
       if(!regtyp(v1))
               return 0;
       v2 = &p->to;
       if(!regtyp(v2))
               return 0;
       for(r=uniqp(r0); r!=R; r=uniqp(r)) {
               if(uniqs(r) == R)
                       break;
               p = r->prog;
               switch(p->as) {
               case ABL:
                       return 0;

               case AADD:
               case AADDC:
               case AADDCC:
               case AADDE:
               case AADDECC:
               case ASUB:
               case ASUBCC:
               case ASUBC:
               case ASUBCCC:
               case ASUBE:
               case ASUBECC:
               case ASLW:
               case ASLWCC:
               case ASRW:
               case ASRWCC:
               case ASRAW:
               case ASRAWCC:
               case AOR:
               case AORCC:
               case AORN:
               case AORNCC:
               case AAND:
               case AANDCC:
               case AANDN:
               case AANDNCC:
               case ANAND:
               case ANANDCC:
               case ANOR:
               case ANORCC:
               case AXOR:
               case AXORCC:
               case AMULHW:
               case AMULHWU:
               case AMULLW:
               case ADIVW:
               case ADIVWU:
               case AREM:
               case AREMU:
               case ARLWNM:
               case ARLWNMCC:

               case AFADD:
               case AFADDS:
               case AFSUB:
               case AFSUBS:
               case AFMUL:
               case AFMULS:
               case AFDIV:
               case AFDIVS:
                       if(p->to.type == v1->type)
                       if(p->to.reg == v1->reg) {
                               if(p->reg == NREG)
                                       p->reg = p->to.reg;
                               goto gotit;
                       }
                       break;

               case AADDME:
               case AADDMECC:
               case AADDZE:
               case AADDZECC:
               case ASUBME:
               case ASUBMECC:
               case ASUBZE:
               case ASUBZECC:
               case ANEG:
               case ANEGCC:
               case AFNEG:
               case AFNEGCC:
               case AFMOVS:
               case AFMOVD:
               case AMOVW:
                       if(p->to.type == v1->type)
                       if(p->to.reg == v1->reg)
                               goto gotit;
                       break;
               }
               if(copyau(&p->from, v2) ||
                  copyau1(p, v2) ||
                  copyau(&p->to, v2))
                       break;
               if(copysub(&p->from, v1, v2, 0) ||
                  copysub1(p, v1, v2, 0) ||
                  copysub(&p->to, v1, v2, 0))
                       break;
       }
       return 0;

gotit:
       copysub(&p->to, v1, v2, 1);
       if(debug['P']) {
               print("gotit: %D->%D\n%P", v1, v2, r->prog);
               if(p->from.type == v2->type)
                       print(" excise");
               print("\n");
       }
       for(r=uniqs(r); r!=r0; r=uniqs(r)) {
               p = r->prog;
               copysub(&p->from, v1, v2, 1);
               copysub1(p, v1, v2, 1);
               copysub(&p->to, v1, v2, 1);
               if(debug['P'])
                       print("%P\n", r->prog);
       }
       t = v1->reg;
       v1->reg = v2->reg;
       v2->reg = t;
       if(debug['P'])
               print("%P last\n", r->prog);
       return 1;
}

/*
* The idea is to remove redundant copies.
*      v1->v2  F=0
*      (use v2 s/v2/v1/)*
*      set v1  F=1
*      use v2  return fail
*      -----------------
*      v1->v2  F=0
*      (use v2 s/v2/v1/)*
*      set v1  F=1
*      set v2  return success
*/
int
copyprop(Reg *r0)
{
       Prog *p;
       Adr *v1, *v2;
       Reg *r;

       p = r0->prog;
       v1 = &p->from;
       v2 = &p->to;
       if(copyas(v1, v2))
               return 1;
       for(r=firstr; r!=R; r=r->link)
               r->active = 0;
       return copy1(v1, v2, r0->s1, 0);
}

copy1(Adr *v1, Adr *v2, Reg *r, int f)
{
       int t;
       Prog *p;

       if(r->active) {
               if(debug['P'])
                       print("act set; return 1\n");
               return 1;
       }
       r->active = 1;
       if(debug['P'])
               print("copy %D->%D f=%d\n", v1, v2, f);
       for(; r != R; r = r->s1) {
               p = r->prog;
               if(debug['P'])
                       print("%P", p);
               if(!f && uniqp(r) == R) {
                       f = 1;
                       if(debug['P'])
                               print("; merge; f=%d", f);
               }
               t = copyu(p, v2, A);
               switch(t) {
               case 2: /* rar, cant split */
                       if(debug['P'])
                               print("; %Drar; return 0\n", v2);
                       return 0;

               case 3: /* set */
                       if(debug['P'])
                               print("; %Dset; return 1\n", v2);
                       return 1;

               case 1: /* used, substitute */
               case 4: /* use and set */
                       if(f) {
                               if(!debug['P'])
                                       return 0;
                               if(t == 4)
                                       print("; %Dused+set and f=%d; return 0\n", v2, f);
                               else
                                       print("; %Dused and f=%d; return 0\n", v2, f);
                               return 0;
                       }
                       if(copyu(p, v2, v1)) {
                               if(debug['P'])
                                       print("; sub fail; return 0\n");
                               return 0;
                       }
                       if(debug['P'])
                               print("; sub%D/%D", v2, v1);
                       if(t == 4) {
                               if(debug['P'])
                                       print("; %Dused+set; return 1\n", v2);
                               return 1;
                       }
                       break;
               }
               if(!f) {
                       t = copyu(p, v1, A);
                       if(!f && (t == 2 || t == 3 || t == 4)) {
                               f = 1;
                               if(debug['P'])
                                       print("; %Dset and !f; f=%d", v1, f);
                       }
               }
               if(debug['P'])
                       print("\n");
               if(r->s2)
                       if(!copy1(v1, v2, r->s2, f))
                               return 0;
       }
       return 1;
}

/*
* return
* 1 if v only used (and substitute),
* 2 if read-alter-rewrite
* 3 if set
* 4 if set and used
* 0 otherwise (not touched)
*/
int
copyu(Prog *p, Adr *v, Adr *s)
{

       switch(p->as) {

       default:
               if(debug['P'])
                       print(" (???)");
               return 2;

       case ANOP:      /* read, write */
       case AMOVW:
       case AMOVH:
       case AMOVHZ:
       case AMOVB:
       case AMOVBZ:

       case ANEG:
       case ANEGCC:
       case AADDME:
       case AADDMECC:
       case AADDZE:
       case AADDZECC:
       case ASUBME:
       case ASUBMECC:
       case ASUBZE:
       case ASUBZECC:

       case AFCTIW:
       case AFCTIWZ:
       case AFMOVS:
       case AFMOVD:
       case AFRSP:
       case AFNEG:
       case AFNEGCC:
               if(s != A) {
                       if(copysub(&p->from, v, s, 1))
                               return 1;
                       if(!copyas(&p->to, v))
                               if(copysub(&p->to, v, s, 1))
                                       return 1;
                       return 0;
               }
               if(copyas(&p->to, v)) {
                       if(copyau(&p->from, v))
                               return 4;
                       return 3;
               }
               if(copyau(&p->from, v))
                       return 1;
               if(copyau(&p->to, v))
                       return 1;
               return 0;

       case ARLWMI:    /* read read rar */
       case ARLWMICC:
               if(copyas(&p->to, v))
                       return 2;
               /* fall through */

       case AADD:      /* read read write */
       case AADDC:
       case AADDE:
       case ASUB:
       case ASLW:
       case ASRW:
       case ASRAW:
       case AOR:
       case AORCC:
       case AORN:
       case AORNCC:
       case AAND:
       case AANDCC:
       case AANDN:
       case AANDNCC:
       case ANAND:
       case ANANDCC:
       case ANOR:
       case ANORCC:
       case AXOR:
       case AMULHW:
       case AMULHWU:
       case AMULLW:
       case ADIVW:
       case ADIVWU:
       case AREM:
       case AREMU:
       case ARLWNM:
       case ARLWNMCC:

       case AFADDS:
       case AFADD:
       case AFSUBS:
       case AFSUB:
       case AFMULS:
       case AFMUL:
       case AFDIVS:
       case AFDIV:
               if(s != A) {
                       if(copysub(&p->from, v, s, 1))
                               return 1;
                       if(copysub1(p, v, s, 1))
                               return 1;
                       if(!copyas(&p->to, v))
                               if(copysub(&p->to, v, s, 1))
                                       return 1;
                       return 0;
               }
               if(copyas(&p->to, v)) {
                       if(p->reg == NREG)
                               p->reg = p->to.reg;
                       if(copyau(&p->from, v))
                               return 4;
                       if(copyau1(p, v))
                               return 4;
                       return 3;
               }
               if(copyau(&p->from, v))
                       return 1;
               if(copyau1(p, v))
                       return 1;
               if(copyau(&p->to, v))
                       return 1;
               return 0;

       case ABEQ:
       case ABGT:
       case ABGE:
       case ABLT:
       case ABLE:
       case ABNE:
       case ABVC:
       case ABVS:
               break;

       case ACMP:      /* read read */
       case ACMPU:
       case AFCMPO:
       case AFCMPU:
               if(s != A) {
                       if(copysub(&p->from, v, s, 1))
                               return 1;
                       return copysub(&p->to, v, s, 1);
               }
               if(copyau(&p->from, v))
                       return 1;
               if(copyau(&p->to, v))
                       return 1;
               break;

       case ABR:       /* funny */
               if(s != A) {
                       if(copysub(&p->to, v, s, 1))
                               return 1;
                       return 0;
               }
               if(copyau(&p->to, v))
                       return 1;
               return 0;

       case ARETURN:   /* funny */
               if(v->type == D_REG)
                       if(v->reg == REGRET)
                               return 2;
               if(v->type == D_FREG)
                       if(v->reg == FREGRET)
                               return 2;

       case ABL:       /* funny */
               if(v->type == D_REG) {
                       if(v->reg <= REGEXT && v->reg > exregoffset)
                               return 2;
                       if(v->reg == REGARG)
                               return 2;
               }
               if(v->type == D_FREG) {
                       if(v->reg <= FREGEXT && v->reg > exfregoffset)
                               return 2;
               }

               if(s != A) {
                       if(copysub(&p->to, v, s, 1))
                               return 1;
                       return 0;
               }
               if(copyau(&p->to, v))
                       return 4;
               return 3;

       case ATEXT:     /* funny */
               if(v->type == D_REG)
                       if(v->reg == REGARG)
                               return 3;
               return 0;
       }
       return 0;
}

int
a2type(Prog *p)
{

       switch(p->as) {
       case AADD:
       case AADDC:
       case AADDCC:
       case AADDCCC:
       case AADDE:
       case AADDECC:
       case AADDME:
       case AADDMECC:
       case AADDZE:
       case AADDZECC:
       case ASUB:
       case ASUBC:
       case ASUBCC:
       case ASUBCCC:
       case ASUBE:
       case ASUBECC:
       case ASUBME:
       case ASUBMECC:
       case ASUBZE:
       case ASUBZECC:
       case ASLW:
       case ASLWCC:
       case ASRW:
       case ASRWCC:
       case ASRAW:
       case ASRAWCC:
       case AOR:
       case AORCC:
       case AORN:
       case AORNCC:
       case AAND:
       case AANDCC:
       case AANDN:
       case AANDNCC:
       case AXOR:
       case AXORCC:
       case ANEG:
       case ANEGCC:
       case AMULHW:
       case AMULHWU:
       case AMULLW:
       case AMULLWCC:
       case ADIVW:
       case ADIVWCC:
       case ADIVWU:
       case ADIVWUCC:
       case AREM:
       case AREMCC:
       case AREMU:
       case AREMUCC:
       case ANAND:
       case ANANDCC:
       case ANOR:
       case ANORCC:
       case ARLWMI:
       case ARLWMICC:
       case ARLWNM:
       case ARLWNMCC:
               return D_REG;

       case AFADDS:
       case AFADDSCC:
       case AFADD:
       case AFADDCC:
       case AFSUBS:
       case AFSUBSCC:
       case AFSUB:
       case AFSUBCC:
       case AFMULS:
       case AFMULSCC:
       case AFMUL:
       case AFMULCC:
       case AFDIVS:
       case AFDIVSCC:
       case AFDIV:
       case AFDIVCC:
       case AFNEG:
       case AFNEGCC:
               return D_FREG;
       }
       return D_NONE;
}

/*
* direct reference,
* could be set/use depending on
* semantics
*/
int
copyas(Adr *a, Adr *v)
{

       if(regtyp(v))
               if(a->type == v->type)
               if(a->reg == v->reg)
                       return 1;
       return 0;
}

/*
* either direct or indirect
*/
int
copyau(Adr *a, Adr *v)
{

       if(copyas(a, v))
               return 1;
       if(v->type == D_REG)
               if(a->type == D_OREG)
                       if(v->reg == a->reg)
                               return 1;
       return 0;
}

int
copyau1(Prog *p, Adr *v)
{

       if(regtyp(v))
               if(p->from.type == v->type || p->to.type == v->type)
               if(p->reg == v->reg) {
                       if(a2type(p) != v->type)
                               print("botch a2type %P\n", p);
                       return 1;
               }
       return 0;
}

/*
* substitute s for v in a
* return failure to substitute
*/
int
copysub(Adr *a, Adr *v, Adr *s, int f)
{

       if(f)
       if(copyau(a, v))
               a->reg = s->reg;
       return 0;
}

int
copysub1(Prog *p1, Adr *v, Adr *s, int f)
{

       if(f)
       if(copyau1(p1, v))
               p1->reg = s->reg;
       return 0;
}