root/src/cmd/5c/peep.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. peep
  2. excise
  3. uniqp
  4. uniqs
  5. regtyp
  6. subprop
  7. copyprop
  8. copy1
  9. constprop
  10. shiftprop
  11. findpre
  12. findinc
  13. nochange
  14. findu1
  15. finduse
  16. xtramodes
  17. copyu
  18. a2type
  19. copyas
  20. copyau
  21. copyau1
  22. copysub
  23. copysub1
  24. isbranch
  25. predicable
  26. modifiescpsr
  27. joinsplit
  28. successor
  29. applypred
  30. predicate

// Inferno utils/5c/peep.c
// http://code.google.com/p/inferno-os/source/browse/utils/5c/peep.c
//
//      Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//      Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
//      Portions Copyright © 1997-1999 Vita Nuova Limited
//      Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
//      Portions Copyright © 2004,2006 Bruce Ellis
//      Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
//      Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
//      Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.


#include "gc.h"

int xtramodes(Reg*, Addr*);

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 == ASLL || p->as == ASRL || p->as == ASRA) {
                        /*
                         * elide shift into D_SHIFT operand of subsequent instruction
                         */
                        if(shiftprop(r)) {
                                excise(r);
                                t++;
                        }
                }
                if(p->as == AMOVW || p->as == AMOVF || p->as == AMOVD)
                if(regtyp(&p->to)) {
                        if(p->from.type == D_CONST)
                                constprop(&p->from, &p->to, r->s1);
                        else 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(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 AEOR:
                        /*
                         * EOR -1,x,y => MVN x,y
                         */
                        if(p->from.type == D_CONST && p->from.offset == -1) {
                                p->as = AMVN;
                                p->from.type = D_REG;
                                if(p->reg != NREG)
                                        p->from.reg = p->reg;
                                else
                                        p->from.reg = p->to.reg;
                                p->reg = NREG;
                        }
                        continue;
                case AMOVH:
                case AMOVHS:
                case AMOVHU:
                case AMOVB:
                case AMOVBS:
                case AMOVBU:
                        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);
        }

        for(r=firstr; r!=R; r=r->link) {
                p = r->prog;
                switch(p->as) {
                case AMOVW:
                case AMOVB:
                case AMOVBS:
                case AMOVBU:
                        if(p->from.type == D_OREG && p->from.offset == 0)
                                xtramodes(r, &p->from);
                        else if(p->to.type == D_OREG && p->to.offset == 0)
                                xtramodes(r, &p->to);
                        else
                                continue;
                        break;
                case ACMP:
                        /*
                         * elide CMP $0,x if calculation of x can set condition codes
                         */
                        if(p->from.type != D_CONST || p->from.offset != 0)
                                continue;
                        r2 = r->s1;
                        if(r2 == R)
                                continue;
                        t = r2->prog->as;
                        switch(t) {
                        default:
                                continue;
                        case ABEQ:
                        case ABNE:
                        case ABMI:
                        case ABPL:
                                break;
                        case ABGE:
                                t = ABPL;
                                break;
                        case ABLT:
                                t = ABMI;
                                break;
                        case ABHI:
                                t = ABNE;
                                break;
                        case ABLS:
                                t = ABEQ;
                                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)
                                continue;
                        if(p1->to.reg != p->reg)
                        if(!(p1->as == AMOVW && p1->from.type == D_REG && p1->from.reg == p->reg))
                                continue;
                        switch(p1->as) {
                        default:
                                continue;
                        case AMOVW:
                                if(p1->from.type != D_REG)
                                        continue;
                        case AAND:
                        case AEOR:
                        case AORR:
                        case ABIC:
                        case AMVN:
                        case ASUB:
                        case ARSB:
                        case AADD:
                        case AADC:
                        case ASBC:
                        case ARSC:
                                break;
                        }
                        p1->scond |= C_SBIT;
                        r2->prog->as = t;
                        excise(r);
                        continue;
                }
        }

        predicate();
}

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

        p = r->prog;
        p->as = ANOP;
        p->scond = zprog.scond;
        p->from = zprog.from;
        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;
}

int
regtyp(Addr *a)
{

        if(a->type == D_REG)
                return 1;
        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;
        Addr *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 ACMP:
                case ACMN:
                case AADD:
                case ASUB:
                case ARSB:
                case ASLL:
                case ASRL:
                case ASRA:
                case AORR:
                case AAND:
                case AEOR:
                case AMUL:
                case ADIV:
                case ADIVU:

                case ACMPF:
                case ACMPD:
                case AADDD:
                case AADDF:
                case ASUBD:
                case ASUBF:
                case AMULD:
                case AMULF:
                case ADIVD:
                case ADIVF:
                        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 AMOVF:
                case AMOVD:
                case AMOVW:
                        if(p->to.type == v1->type)
                        if(p->to.reg == v1->reg)
                                goto gotit;
                        break;

                case AMOVM:
                        t = 1<<v2->reg;
                        if((p->from.type == D_CONST && (p->from.offset&t)) ||
                           (p->to.type == D_CONST && (p->to.offset&t)))
                                return 0;
                        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;
        Addr *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);
}

int
copy1(Addr *v1, Addr *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, can't 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;
}

/*
 * The idea is to remove redundant constants.
 *      $c1->v1
 *      ($c1->v2 s/$c1/v1)*
 *      set v1  return
 * The v1->v2 should be eliminated by copy propagation.
 */
void
constprop(Addr *c1, Addr *v1, Reg *r)
{
        Prog *p;

        if(debug['C'])
                print("constprop %D->%D\n", c1, v1);
        for(; r != R; r = r->s1) {
                p = r->prog;
                if(debug['C'])
                        print("%P", p);
                if(uniqp(r) == R) {
                        if(debug['C'])
                                print("; merge; return\n");
                        return;
                }
                if(p->as == AMOVW && copyas(&p->from, c1)) {
                                if(debug['C'])
                                        print("; sub%D/%D", &p->from, v1);
                                p->from = *v1;
                } else if(copyu(p, v1, A) > 1) {
                        if(debug['C'])
                                print("; %Dset; return\n", v1);
                        return;
                }
                if(debug['C'])
                        print("\n");
                if(r->s2)
                        constprop(c1, v1, r->s2);
        }
}

/*
 * ASLL x,y,w
 * .. (not use w, not set x y w)
 * AXXX w,a,b (a != w)
 * .. (not use w)
 * (set w)
 * ----------- changed to
 * ..
 * AXXX (x<<y),a,b
 * ..
 */
#define FAIL(msg) { if(debug['H']) print("\t%s; FAILURE\n", msg); return 0; }
int
shiftprop(Reg *r)
{
        Reg *r1;
        Prog *p, *p1, *p2;
        int n, o;
        Addr a;

        p = r->prog;
        if(p->to.type != D_REG)
                FAIL("BOTCH: result not reg");
        n = p->to.reg;
        a = zprog.from;
        if(p->reg != NREG && p->reg != p->to.reg) {
                a.type = D_REG;
                a.reg = p->reg;
        }
        if(debug['H'])
                print("shiftprop\n%P", p);
        r1 = r;
        for(;;) {
                /* find first use of shift result; abort if shift operands or result are changed */
                r1 = uniqs(r1);
                if(r1 == R)
                        FAIL("branch");
                if(uniqp(r1) == R)
                        FAIL("merge");
                p1 = r1->prog;
                if(debug['H'])
                        print("\n%P", p1);
                switch(copyu(p1, &p->to, A)) {
                case 0: /* not used or set */
                        if((p->from.type == D_REG && copyu(p1, &p->from, A) > 1) ||
                           (a.type == D_REG && copyu(p1, &a, A) > 1))
                                FAIL("args modified");
                        continue;
                case 3: /* set, not used */
                        FAIL("BOTCH: noref");
                }
                break;
        }
        /* check whether substitution can be done */
        switch(p1->as) {
        default:
                FAIL("non-dpi");
        case AAND:
        case AEOR:
        case AADD:
        case AADC:
        case AORR:
        case ASUB:
        case ARSB:
        case ASBC:
        case ARSC:
                if(p1->reg == n || (p1->reg == NREG && p1->to.type == D_REG && p1->to.reg == n)) {
                        if(p1->from.type != D_REG)
                                FAIL("can't swap");
                        p1->reg = p1->from.reg;
                        p1->from.reg = n;
                        switch(p1->as) {
                        case ASUB:
                                p1->as = ARSB;
                                break;
                        case ARSB:
                                p1->as = ASUB;
                                break;
                        case ASBC:
                                p1->as = ARSC;
                                break;
                        case ARSC:
                                p1->as = ASBC;
                                break;
                        }
                        if(debug['H'])
                                print("\t=>%P", p1);
                }
        case ABIC:
        case ACMP:
        case ACMN:
                if(p1->reg == n)
                        FAIL("can't swap");
                if(p1->reg == NREG && p1->to.reg == n)
                        FAIL("shift result used twice");
        case AMVN:
                if(p1->from.type == D_SHIFT)
                        FAIL("shift result used in shift");
                if(p1->from.type != D_REG || p1->from.reg != n)
                        FAIL("BOTCH: where is it used?");
                break;
        }
        /* check whether shift result is used subsequently */
        p2 = p1;
        if(p1->to.reg != n)
        for (;;) {
                r1 = uniqs(r1);
                if(r1 == R)
                        FAIL("inconclusive");
                p1 = r1->prog;
                if(debug['H'])
                        print("\n%P", p1);
                switch(copyu(p1, &p->to, A)) {
                case 0: /* not used or set */
                        continue;
                case 3: /* set, not used */
                        break;
                default:/* used */
                        FAIL("reused");
                }
                break;
        }
        /* make the substitution */
        p2->from.type = D_SHIFT;
        p2->from.reg = NREG;
        o = p->reg;
        if(o == NREG)
                o = p->to.reg;
        switch(p->from.type){
        case D_CONST:
                o |= (p->from.offset&0x1f)<<7;
                break;
        case D_REG:
                o |= (1<<4) | (p->from.reg<<8);
                break;
        }
        switch(p->as){
        case ASLL:
                o |= 0<<5;
                break;
        case ASRL:
                o |= 1<<5;
                break;
        case ASRA:
                o |= 2<<5;
                break;
        }
        p2->from.offset = o;
        if(debug['H'])
                print("\t=>%P\tSUCCEED\n", p2);
        return 1;
}

Reg*
findpre(Reg *r, Addr *v)
{
        Reg *r1;

        for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) {
                if(uniqs(r1) != r)
                        return R;
                switch(copyu(r1->prog, v, A)) {
                case 1: /* used */
                case 2: /* read-alter-rewrite */
                        return R;
                case 3: /* set */
                case 4: /* set and used */
                        return r1;
                }
        }
        return R;
}

Reg*
findinc(Reg *r, Reg *r2, Addr *v)
{
        Reg *r1;
        Prog *p;


        for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) {
                if(uniqp(r1) != r)
                        return R;
                switch(copyu(r1->prog, v, A)) {
                case 0: /* not touched */
                        continue;
                case 4: /* set and used */
                        p = r1->prog;
                        if(p->as == AADD)
                        if(p->from.type == D_CONST)
                        if(p->from.offset > -4096 && p->from.offset < 4096)
                                return r1;
                default:
                        return R;
                }
        }
        return R;
}

int
nochange(Reg *r, Reg *r2, Prog *p)
{
        Addr a[3];
        int i, n;

        if(r == r2)
                return 1;
        n = 0;
        if(p->reg != NREG && p->reg != p->to.reg) {
                a[n].type = D_REG;
                a[n++].reg = p->reg;
        }
        switch(p->from.type) {
        case D_SHIFT:
                a[n].type = D_REG;
                a[n++].reg = p->from.offset&0xf;
        case D_REG:
                a[n].type = D_REG;
                a[n++].reg = p->from.reg;
        }
        if(n == 0)
                return 1;
        for(; r!=R && r!=r2; r=uniqs(r)) {
                p = r->prog;
                for(i=0; i<n; i++)
                        if(copyu(p, &a[i], A) > 1)
                                return 0;
        }
        return 1;
}

int
findu1(Reg *r, Addr *v)
{
        for(; r != R; r = r->s1) {
                if(r->active)
                        return 0;
                r->active = 1;
                switch(copyu(r->prog, v, A)) {
                case 1: /* used */
                case 2: /* read-alter-rewrite */
                case 4: /* set and used */
                        return 1;
                case 3: /* set */
                        return 0;
                }
                if(r->s2)
                        if (findu1(r->s2, v))
                                return 1;
        }
        return 0;
}

int
finduse(Reg *r, Addr *v)
{
        Reg *r1;

        for(r1=firstr; r1!=R; r1=r1->link)
                r1->active = 0;
        return findu1(r, v);
}

int
xtramodes(Reg *r, Addr *a)
{
        Reg *r1, *r2, *r3;
        Prog *p, *p1;
        Addr v;

        p = r->prog;
        if((p->as == AMOVB || p->as == AMOVBS) && p->from.type == D_OREG)       /* byte load */
                return 0;
        v = *a;
        v.type = D_REG;
        r1 = findpre(r, &v);
        if(r1 != R) {
                p1 = r1->prog;
                if(p1->to.type == D_REG && p1->to.reg == v.reg)
                switch(p1->as) {
                case AADD:
                        if(p1->from.type == D_REG ||
                           (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 &&
                            ((p->as != AMOVB && p->as != AMOVBS) || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||
                           (p1->from.type == D_CONST &&
                            p1->from.offset > -4096 && p1->from.offset < 4096))
                        if(nochange(uniqs(r1), r, p1)) {
                                if(a != &p->from || v.reg != p->to.reg)
                                if (finduse(r->s1, &v)) {
                                        if(p1->reg == NREG || p1->reg == v.reg)
                                                /* pre-indexing */
                                                p->scond |= C_WBIT;
                                        else return 0;
                                }
                                switch (p1->from.type) {
                                case D_REG:
                                        /* register offset */
                                        a->type = D_SHIFT;
                                        a->offset = p1->from.reg;
                                        break;
                                case D_SHIFT:
                                        /* scaled register offset */
                                        a->type = D_SHIFT;
                                case D_CONST:
                                        /* immediate offset */
                                        a->offset = p1->from.offset;
                                        break;
                                }
                                if(p1->reg != NREG)
                                        a->reg = p1->reg;
                                excise(r1);
                                return 1;
                        }
                        break;
                case AMOVW:
                        if(p1->from.type == D_REG)
                        if((r2 = findinc(r1, r, &p1->from)) != R) {
                        for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3))
                                ;
                        if(r3 == r) {
                                /* post-indexing */
                                p1 = r2->prog;
                                a->reg = p1->to.reg;
                                a->offset = p1->from.offset;
                                p->scond |= C_PBIT;
                                if(!finduse(r, &r1->prog->to))
                                        excise(r1);
                                excise(r2);
                                return 1;
                        }
                        }
                        break;
                }
        }
        if(a != &p->from || a->reg != p->to.reg)
        if((r1 = findinc(r, R, &v)) != R) {
                /* post-indexing */
                p1 = r1->prog;
                a->offset = p1->from.offset;
                p->scond |= C_PBIT;
                excise(r1);
                return 1;
        }
        return 0;
}

/*
 * 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, Addr *v, Addr *s)
{

        switch(p->as) {

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

        case AMOVM:
                if(v->type != D_REG)
                        return 0;
                if(p->from.type == D_CONST) {   /* read reglist, read/rar */
                        if(s != A) {
                                if(p->from.offset&(1<<v->reg))
                                        return 1;
                                if(copysub(&p->to, v, s, 1))
                                        return 1;
                                return 0;
                        }
                        if(copyau(&p->to, v)) {
                                if(p->scond&C_WBIT)
                                        return 2;
                                return 1;
                        }
                        if(p->from.offset&(1<<v->reg))
                                return 1;
                } else {                        /* read/rar, write reglist */
                        if(s != A) {
                                if(p->to.offset&(1<<v->reg))
                                        return 1;
                                if(copysub(&p->from, v, s, 1))
                                        return 1;
                                return 0;
                        }
                        if(copyau(&p->from, v)) {
                                if(p->scond&C_WBIT)
                                        return 2;
                                if(p->to.offset&(1<<v->reg))
                                        return 4;
                                return 1;
                        }
                        if(p->to.offset&(1<<v->reg))
                                return 3;
                }
                return 0;

        case ANOP:      /* read, write */
        case AMOVW:
        case AMOVF:
        case AMOVD:
        case AMOVH:
        case AMOVHS:
        case AMOVHU:
        case AMOVB:
        case AMOVBS:
        case AMOVBU:
        case AMOVDW:
        case AMOVWD:
        case AMOVFD:
        case AMOVDF:
                if(p->scond&(C_WBIT|C_PBIT))
                if(v->type == D_REG) {
                        if(p->from.type == D_OREG || p->from.type == D_SHIFT) {
                                if(p->from.reg == v->reg)
                                        return 2;
                        } else {
                                if(p->to.reg == v->reg)
                                return 2;
                        }
                }
                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 AADD:      /* read, read, write */
        case ASUB:
        case ARSB:
        case ASLL:
        case ASRL:
        case ASRA:
        case AORR:
        case AAND:
        case AEOR:
        case AMUL:
        case ADIV:
        case ADIVU:
        case AADDF:
        case AADDD:
        case ASUBF:
        case ASUBD:
        case AMULF:
        case AMULD:
        case ADIVF:
        case ADIVD:

        case ACMPF:
        case ACMPD:
        case ACMP:
        case ACMN:
        case ACASE:
                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:      /* read, read */
        case ABNE:
        case ABCS:
        case ABHS:
        case ABCC:
        case ABLO:
        case ABMI:
        case ABPL:
        case ABVS:
        case ABVC:
        case ABHI:
        case ABLS:
        case ABGE:
        case ABLT:
        case ABGT:
        case ABLE:
        case APLD:
                if(s != A) {
                        if(copysub(&p->from, v, s, 1))
                                return 1;
                        return copysub1(p, v, s, 1);
                }
                if(copyau(&p->from, v))
                        return 1;
                if(copyau1(p, v))
                        return 1;
                return 0;

        case AB:        /* 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 ARET:      /* 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;
        }
}

int
a2type(Prog *p)
{

        switch(p->as) {

        case ACMP:
        case ACMN:

        case AADD:
        case ASUB:
        case ARSB:
        case ASLL:
        case ASRL:
        case ASRA:
        case AORR:
        case AAND:
        case AEOR:
        case AMUL:
        case ADIV:
        case ADIVU:
                return D_REG;

        case ACMPF:
        case ACMPD:

        case AADDF:
        case AADDD:
        case ASUBF:
        case ASUBD:
        case AMULF:
        case AMULD:
        case ADIVF:
        case ADIVD:
                return D_FREG;
        }
        return D_NONE;
}

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

        if(regtyp(v)) {
                if(a->type == v->type)
                if(a->reg == v->reg)
                        return 1;
        } else if(v->type == D_CONST) {         /* for constprop */
                if(a->type == v->type)
                if(a->name == v->name)
                if(a->sym == v->sym)
                if(a->reg == v->reg)
                if(a->offset == v->offset)
                        return 1;
        }
        return 0;
}

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

        if(copyas(a, v))
                return 1;
        if(v->type == D_REG) {
                if(a->type == D_OREG) {
                        if(v->reg == a->reg)
                                return 1;
                } else if(a->type == D_SHIFT) {
                        if((a->offset&0xf) == v->reg)
                                return 1;
                        if((a->offset&(1<<4)) && (a->offset>>8) == v->reg)
                                return 1;
                }
        }
        return 0;
}

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

        if(regtyp(v)) {
                if(a2type(p) == 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(Addr *a, Addr *v, Addr *s, int f)
{

        if(f)
        if(copyau(a, v)) {
                if(a->type == D_SHIFT) {
                        if((a->offset&0xf) == v->reg)
                                a->offset = (a->offset&~0xf)|s->reg;
                        if((a->offset&(1<<4)) && (a->offset>>8) == v->reg)
                                a->offset = (a->offset&~(0xf<<8))|(s->reg<<8);
                } else
                        a->reg = s->reg;
        }
        return 0;
}

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

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

struct {
        int opcode;
        int notopcode;
        int scond;
        int notscond;
} predinfo[]  = {
        { ABEQ, ABNE,   0x0,    0x1, },
        { ABNE, ABEQ,   0x1,    0x0, },
        { ABCS, ABCC,   0x2,    0x3, },
        { ABHS, ABLO,   0x2,    0x3, },
        { ABCC, ABCS,   0x3,    0x2, },
        { ABLO, ABHS,   0x3,    0x2, },
        { ABMI, ABPL,   0x4,    0x5, },
        { ABPL, ABMI,   0x5,    0x4, },
        { ABVS, ABVC,   0x6,    0x7, },
        { ABVC, ABVS,   0x7,    0x6, },
        { ABHI, ABLS,   0x8,    0x9, },
        { ABLS, ABHI,   0x9,    0x8, },
        { ABGE, ABLT,   0xA,    0xB, },
        { ABLT, ABGE,   0xB,    0xA, },
        { ABGT, ABLE,   0xC,    0xD, },
        { ABLE, ABGT,   0xD,    0xC, },
};

typedef struct {
        Reg *start;
        Reg *last;
        Reg *end;
        int len;
} Joininfo;

enum {
        Join,
        Split,
        End,
        Branch,
        Setcond,
        Toolong
};

enum {
        Falsecond,
        Truecond,
        Delbranch,
        Keepbranch
};

int
isbranch(Prog *p)
{
        return (ABEQ <= p->as) && (p->as <= ABLE);
}

int
predicable(Prog *p)
{
        if (isbranch(p)
                || p->as == ANOP
                || p->as == AXXX
                || p->as == ADATA
                || p->as == AGLOBL
                || p->as == AGOK
                || p->as == AHISTORY
                || p->as == ANAME
                || p->as == ASIGNAME
                || p->as == ATEXT
                || p->as == AWORD
                || p->as == ABCASE
                || p->as == ACASE)
                return 0;
        return 1;
}

/*
 * Depends on an analysis of the encodings performed by 5l.
 * These seem to be all of the opcodes that lead to the "S" bit
 * being set in the instruction encodings.
 *
 * C_SBIT may also have been set explicitly in p->scond.
 */
int
modifiescpsr(Prog *p)
{
        return (p->scond&C_SBIT)
                || p->as == ATST
                || p->as == ATEQ
                || p->as == ACMN
                || p->as == ACMP
                || p->as == AMULU
                || p->as == ADIVU
                || p->as == AMUL
                || p->as == ADIV
                || p->as == AMOD
                || p->as == AMODU
                || p->as == ABL;
}

/*
 * Find the maximal chain of instructions starting with r which could
 * be executed conditionally
 */
int
joinsplit(Reg *r, Joininfo *j)
{
        j->start = r;
        j->last = r;
        j->len = 0;
        do {
                if (r->p2 && (r->p1 || r->p2->p2link)) {
                        j->end = r;
                        return Join;
                }
                if (r->s1 && r->s2) {
                        j->end = r;
                        return Split;
                }
                j->last = r;
                if (r->prog->as != ANOP)
                        j->len++;
                if (!r->s1 && !r->s2) {
                        j->end = r->link;
                        return End;
                }
                if (r->s2) {
                        j->end = r->s2;
                        return Branch;
                }
                if (modifiescpsr(r->prog)) {
                        j->end = r->s1;
                        return Setcond;
                }
                r = r->s1;
        } while (j->len < 4);
        j->end = r;
        return Toolong;
}

Reg *
successor(Reg *r)
{
        if (r->s1)
                return r->s1;
        else
                return r->s2;
}

void
applypred(Reg *rstart, Joininfo *j, int cond, int branch)
{
        int pred;
        Reg *r;

        if(j->len == 0)
                return;
        if (cond == Truecond)
                pred = predinfo[rstart->prog->as - ABEQ].scond;
        else
                pred = predinfo[rstart->prog->as - ABEQ].notscond;

        for (r = j->start; ; r = successor(r)) {
                if (r->prog->as == AB) {
                        if (r != j->last || branch == Delbranch)
                                excise(r);
                        else {
                          if (cond == Truecond)
                                r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode;
                          else
                                r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode;
                        }
                }
                else if (predicable(r->prog))
                        r->prog->scond = (r->prog->scond&~C_SCOND)|pred;
                if (r->s1 != r->link) {
                        r->s1 = r->link;
                        r->link->p1 = r;
                }
                if (r == j->last)
                        break;
        }
}

void
predicate(void)
{
        Reg *r;
        int t1, t2;
        Joininfo j1, j2;

        for(r=firstr; r!=R; r=r->link) {
                if (isbranch(r->prog)) {
                        t1 = joinsplit(r->s1, &j1);
                        t2 = joinsplit(r->s2, &j2);
                        if(j1.last->link != j2.start)
                                continue;
                        if(j1.end == j2.end)
                        if((t1 == Branch && (t2 == Join || t2 == Setcond)) ||
                           (t2 == Join && (t1 == Join || t1 == Setcond))) {
                                applypred(r, &j1, Falsecond, Delbranch);
                                applypred(r, &j2, Truecond, Delbranch);
                                excise(r);
                                continue;
                        }
                        if(t1 == End || t1 == Branch) {
                                applypred(r, &j1, Falsecond, Keepbranch);
                                excise(r);
                                continue;
                        }
                }
        }
}

/* [<][>][^][v][top][bottom][index][help] */