/*
* 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;
case ACMP:
case ACMN:
case AADD:
case ASUB:
case ARSB:
case ASLL:
case ASRL:
case ASRA:
case AROR:
case AORR:
case AAND:
case AEOR:
case AMUL:
case AMULU:
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 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(Adr *c1, Adr *v1, Reg *r)
{
Prog *p;
/*
* 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;
Adr 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;
case AROR:
o |= 3<<5;
break;
}
p2->from.offset = o;
if(debug['H'])
print("\t=>%P\tSUCCEED\n", p2);
return 1;
}
Reg*
findpre(Reg *r, Adr *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;
}
int
findu1(Reg *r, Adr *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;
}
/*
* 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)
{
case ANOP: /* read, write */
case AMOVW:
case AMOVF:
case AMOVD:
case AMOVH:
case AMOVHU:
case AMOVB:
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 AROR:
case AORR:
case AAND:
case AEOR:
case AMUL:
case AMULU:
case ADIV:
case ADIVU:
case AADDF:
case AADDD:
case ASUBF:
case ASUBD:
case AMULF:
case AMULD:
case ADIVF:
case ADIVD:
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:
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 AADD:
case ASUB:
case ARSB:
case ASLL:
case ASRL:
case ASRA:
case AROR:
case AORR:
case AAND:
case AEOR:
case AMUL:
case AMULU:
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(Adr *a, Adr *v)
{
/*
* 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;
}
switch(r->prog->as){
case ADIV:
case ADIVU:
case AMOD:
case AMODU:
/* emulated by 5l, doesnt handle conditionals */
j->end = r->s1;
return Toolong;
}
if (modifiescpsr(r->prog)) {
j->end = r->s1;
return Setcond;
}
r = r->s1;
} while (j->len < 4);
j->end = r;
return Toolong;
}