/* Copyright (c) 1988 by Sozobon, Limited. Author: Tony Andrews * Patched -- PvO -- see file patch * * * Permission is granted to anyone to use this software for any purpose * on any computer system, and to redistribute it freely, with the * following restrictions: * 1) No charge may be made other than reasonable charges for reproduction. * 2) Modified versions must be clearly marked as such. * 3) The authors are not responsible for any harmful consequences * of using this software, even if they result from defects in it. */ /* * 2-instruction peephole optimizations */ #include "top.h" /* * ipeep2(bp, i1) - look for 2-instruction optimizations at the given inst. */ static bool ipeep2(bp, i1) BLOCK *bp; register INST *i1; { register INST *i2; /* the next instruction */ register INST *ti2; /* "temporary" next inst */ register int op1, op2; /* opcodes, for speed */ register int sm1, dm1; /* src, dst amode inst. 1 */ register int dr1; /* dest. reg. inst. 1 */ i2 = i1->next; op1 = i1->opcode; op2 = i2->opcode; sm1 = i1->src.amode; dm1 = i1->dst.amode; dr1 = i1->dst.areg; /* * Avoid stack fix-ups after a call if possible. */ /* * addq #4,sp * ... stuff that doesn't use SP ... * move.l ?,-(sp) => move.l ?,(sp) */ if (op1 == ADDQ && sm1 == IMM && i1->src.disp == 4 && dm1 == REG && dr1 == SP) { ti2 = i2; while (!uses(ti2, SP)) { if (ti2->next == NULL) goto end2; ti2 = ti2->next; } if (ti2->opcode == MOVE && ti2->flags == LENL && ti2->dst.amode == (REGI|DEC) && ti2->dst.areg == SP) { ti2->dst.amode = REGI; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } end2: /* * addq #2,sp * ... stuff that doesn't use SP ... * move.w ?,-(sp) => move.w ?,(sp) */ if (op1 == ADDQ && sm1 == IMM && i1->src.disp == 2 && dm1 == REG && dr1 == SP) { ti2 = i2; while (!uses(ti2, SP)) { if (ti2->next == NULL) goto end3; ti2 = ti2->next; } if (ti2->opcode == MOVE && ti2->flags == LENW && ti2->dst.amode == (REGI|DEC) && ti2->dst.areg == SP) { ti2->dst.amode = REGI; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } end3: /* * Avoid "tst" instructions following instructions that * set the Z flag. */ /* * move.x X, Y => move.x X, Y * tst.x X or Y ...deleted... * beq/bne beq/bne * * Where Y is not An, because "movea" doesn't set the * zero flag. */ if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) && op1 == MOVE && op2 == TST && i1->flags == i2->flags) { /* * If pre-decrement is set on the dest. of the move, * don't let that screw up the operand comparison. */ if (dm1 & DEC) dm1 &= ~DEC; if (opeq(&i1->dst, &i2->src) || opeq(&i1->src, &i2->src)) { if (dm1 != REG || ISD(dr1)) { delinst(bp, i2); DBG(printf("%d ", __LINE__)) return TRUE; } } } /* * and.x X, Y => and.x X, Y * tst.x X or Y ...deleted... * beq/bne beq/bne * * Where Y is not An, because "movea" doesn't set the * zero flag. */ if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) && op1 == AND && op2 == TST && i1->flags == i2->flags) { /* * If pre-decrement is set on the dest. of the move, * don't let that screw up the operand comparison. */ if (dm1 & DEC) dm1 &= ~DEC; if (opeq(&i1->dst, &i2->src) || opeq(&i1->src, &i2->src)) { if (dm1 != REG || ISD(dr1)) { delinst(bp, i2); DBG(printf("%d ", __LINE__)) return TRUE; } } } /* * ext.x Dn => ext.x Dn * tst.x Dn ...deleted... * beq/bne beq/bne * * Where Y is not An, because "movea" doesn't set the * zero flag. */ if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) && op1 == EXT && op2 == TST && i1->flags == i2->flags) { if (sm1 == REG && ISD(i1->src.areg) && i2->src.amode == REG && i1->src.areg == i2->src.areg) { delinst(bp, i2); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * move.? X, Dn => move.? X, Dn * ext.? Dn ...deleted... * beq/bne beq/bne * * Where Dn is dead after the "ext". */ if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) && op1 == MOVE && op2 == EXT) { if (dm1 == REG && ISD(dr1) && i2->src.amode == REG && dr1 == i2->src.areg) { if ((i2->live & RM(i2->src.areg)) == 0) { delinst(bp, i2); DBG(printf("%d ", __LINE__)) return TRUE; } } } /* * ext.l Dm => ...deleted... * tst.l Dm tst.w Dm * * where Dm is dead after the "tst". */ if (op1 == EXT && op2 == TST && ((i1->flags & LENL) != 0) && ((i2->flags & LENL) != 0) && (i1->src.areg == i2->src.areg) && ISD(i1->src.areg)) { if ((i2->live & RM(i2->src.areg)) == 0) { i2->flags = LENW; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * ext.l Dm => ...deleted... * ??? N(An,Dm.l), ?? ??? N(An,Dm.w), ?? * * Where Dm is dead */ if ((op1 == EXT) && (i1->flags & LENL) && (i2->src.amode == (REGIDX|XLONG)) && (i1->src.areg == i2->src.ireg)) { if ((i2->live & RM(i1->src.areg)) == 0) { i2->src.amode &= ~XLONG; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * ext.l Dm => ...deleted... * ??? ??, N(An,Dm.l) ??? ??, N(An,Dm.w) * * Where Dm is dead */ if ((op1 == EXT) && (i1->flags & LENL) && (i2->dst.amode == (REGIDX|XLONG)) && (i1->src.areg == i2->dst.ireg)) { if ((i2->live & RM(i1->src.areg)) == 0) { i2->dst.amode &= ~XLONG; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * Avoid intermediate registers. */ /* * move.x X, Dm => INST.x X, Dn * INST.x Dm, Dn * * where Dm is dead, and INST is one of: add, sub, and, or, cmp */ if ((op1 == MOVE) && ((op2==ADD)||(op2==SUB)||(op2==AND)||(op2==OR)||(op2==CMP)) && (i1->flags == i2->flags) && (dm1 == REG) && ISD(dr1) && (i2->src.amode == REG) && ISD(i2->src.areg) && (dr1 == i2->src.areg) && (i2->dst.amode == REG) && ISD(i2->dst.areg)) { if ((i2->live & RM(i2->src.areg)) == 0) { i1->opcode = i2->opcode; i1->dst.areg = i2->dst.areg; delinst(bp, i2); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * Silly moves */ /* * move.x X, Y => move.x X, Y * move.x Y, X */ if ((op1 == MOVE) && (op2 == MOVE) && (i1->flags == i2->flags) && opeq(&i1->src, &i2->dst) && opeq(&i1->dst, &i2->src) && ((i1->src.amode & (INC|DEC)) == 0) && ((i1->dst.amode & (INC|DEC)) == 0)) { delinst(bp, i2); DBG(printf("%d ", __LINE__)) return TRUE; } /* * move.x X, Y => move.x X, Rn * move.x Y, Rn move.x Rn, Y * * where Y isn't INC or DEC, and isn't register direct */ if ((op1 == MOVE) && (op2 == MOVE) && (i2->dst.amode == REG) && opeq(&i1->dst, &i2->src) && ((i1->dst.amode & (INC|DEC)) == 0) && (i1->flags == i2->flags) && (i1->dst.amode != REG) && !uses(i1, i2->dst.areg)) { freeop(&i1->dst); i1->dst = i2->dst; i2->dst = i2->src; i2->src = i1->dst; DBG(printf("%d ", __LINE__)) return TRUE; } /* * move.x Dm, X => move.x Dm, X * move.x X, Y move.x Dm, Y * * Where 'x' is the same, and 'X' has no side-effects. */ if ((op1 == MOVE) && (op2 == MOVE) && (sm1 == REG) && ISD(i1->src.areg) && (i1->flags == i2->flags) && opeq(&i1->dst, &i2->src) && ((dm1 & (DEC|INC)) == 0)) { freeop(&i2->src); i2->src = i1->src; DBG(printf("%d ", __LINE__)) return TRUE; } /* * move.? Rm, Rn => move.? Rm, Rn * ... stuff ... ... stuff ... * move.? Rm, Rn * * where "stuff" doesn't set Rm or Rn. Also make sure that * the second move isn't followed by a conditional branch. * In that case leave everything alone since the branch * probably relies on flags set by the move. */ if ((op1 == MOVE) && (sm1 == REG) && (dm1 == REG)) { int s1 = i1->src.areg; /* source reg of inst. 1 */ ti2 = i2; while (ti2 != NULL && !sets(ti2, s1) && !sets(ti2, dr1)) { if ((ti2->opcode==MOVE) && (i1->flags==ti2->flags) && (ti2->src.amode==REG) && (ti2->dst.amode==REG) && (i1->src.areg == ti2->src.areg) && (i1->dst.areg == ti2->dst.areg) && ((bp->last != ti2) || (bp->bcond == NULL)) ) { delinst(bp, ti2); DBG(printf("%d ", __LINE__)) return TRUE; } ti2 = ti2->next; } } /* * move.l Am, Dn => move.l Am, Ao * ... stuff ... ... stuff ... * move.l Dn, Ao * * where "stuff" doesn't set Dn. */ if ((op1 == MOVE) && (i1->flags == LENL) && (sm1 == REG) && ISA(i1->src.areg) && (dm1 == REG) && ISD(dr1)) { ti2 = i2; while (!sets(ti2, dr1)) { if ((ti2->opcode == MOVE) && (ti2->flags == LENL) && (ti2->src.amode == REG) && ISD(ti2->src.areg) && (ti2->dst.amode == REG) && ISA(ti2->dst.areg) && (dr1 == ti2->src.areg)) { /* * If the intermediate register isn't dead, * then we have to keep using it. */ if ((ti2->live & RM(ti2->src.areg)) != 0) goto end14; i1->dst.areg = ti2->dst.areg; delinst(bp, ti2); DBG(printf("%d ", __LINE__)) return TRUE; } if (ti2->next == NULL) goto end14; ti2 = ti2->next; } } end14: /* * move.l Dm, An => move.l Dm, Ao * lea (An), Ao * * where An is dead */ if ((op1 == MOVE) && (op2 == LEA) && (sm1 == REG) && ISD(i1->src.areg) && (dm1 == REG) && ISA(dr1) && (i2->src.amode == REGI) && (i2->dst.amode == REG) && ISA(i2->dst.areg) && (dr1 == i2->src.areg)) { if ((i2->live & RM(i2->src.areg)) == 0) { i1->dst.areg = i2->dst.areg; delinst(bp, i2); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * lea X, An => lea X, Ao * lea (An), Ao * * where An is dead */ if ((op1 == LEA) && (op2 == LEA) && (i2->src.amode == REGI) && (dr1 == i2->src.areg)) { if ((i2->live & RM(i2->src.areg)) == 0) { i1->dst.areg = i2->dst.areg; delinst(bp, i2); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * lea N(Am), Am => * ? (Am)[,...] ? N(Am)[,...] * * Where Am is either dead after the second instruction or * is a direct destination of the second instruction. */ if ((op1 == LEA) && (i1->src.amode == REGID) && (i1->src.areg == i1->dst.areg) && (i2->src.amode == REGI) && (i1->dst.areg == i2->src.areg)) { if (((i2->live & RM(i2->src.areg)) == 0) || ((i2->dst.amode == REG) && (i2->dst.areg == i2->src.areg))) { i2->src.amode = REGID; i2->src.disp = i1->src.disp; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * lea N(Am), Am => * ? X, (Am) ? X, N(Am) * * Where X doesn't reference Am, and Am is dead after the * second instruction. */ if ((op1 == LEA) && (i1->src.amode == REGID) && (i1->src.areg == i1->dst.areg) && (i2->dst.amode == REGI) && (i1->dst.areg == i2->dst.areg)) { if (((i2->live & RM(i2->dst.areg)) == 0) && ((i2->src.amode == IMM) || (i2->src.amode == ABS) || (i2->src.areg != i2->dst.areg))) { i2->dst.amode = REGID; i2->dst.disp = i1->src.disp; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * lea X, Am => ...deleted... * clr.x (Am) clr.x X * * where Am is dead */ if ((op1 == LEA) && (op2 == CLR) && i2->src.amode == REGI && (i1->dst.areg == i2->src.areg)) { if ((i2->live & RM(i2->src.areg)) == 0) { i2->src = i1->src; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * lea X, Am => ...deleted... * move.x Y, (Am) move.x Y, X * * where Am is dead */ if ((op1 == LEA) && (op2 == MOVE) && i2->dst.amode == REGI && (i1->dst.areg == i2->dst.areg)) { if ((i2->live & RM(i2->dst.areg)) == 0) { i2->dst = i1->src; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * lea X, Am => ...deleted... * move.x (Am), Y move.x X, Y * * where Am is dead */ if ((op1 == LEA) && (op2 == MOVE) && i2->src.amode == REGI && (i1->dst.areg == i2->src.areg)) { if ((i2->live & RM(i2->src.areg)) == 0) { i2->src = i1->src; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * move.x Dm, X => move.x Dm, X * cmp.x #N, X cmp.x #N, Dm * * Where X isn't register direct. * * Since X generally references memory, we can compare * with the register faster. */ if ((op1 == MOVE) && (op2 == CMP) && (i1->flags == i2->flags) && (i2->src.amode == IMM) && (sm1 == REG) && ISD(i1->src.areg) && (dm1 != REG) && opeq(&i1->dst, &i2->dst) && ((dm1 & (INC|DEC)) == 0)) { freeop(&i2->dst); i2->dst.amode = REG; i2->dst.areg = i1->src.areg; DBG(printf("%d ", __LINE__)) return TRUE; } /* * Try to use register indirect w/ displacement and/or index */ /* * add.l Am, Dn => lea 0(Am,Dn.l), Ao * move.l Dn, Ao * * where Dn is dead */ if ((op1 == ADD) && (op2 == MOVE) && (sm1 == REG) && ISA(i1->src.areg) && (dm1 == REG) && ISD(dr1) && (i2->src.amode == REG) && ISD(i2->src.areg) && (i2->dst.amode == REG) && ISA(i2->dst.areg) && (dr1 == i2->src.areg) && (i1->flags & LENL) && (i2->flags & LENL)) { if ((i2->live & RM(i2->src.areg)) == 0) { i2->opcode = LEA; i2->flags = 0; i2->src.amode = REGIDX|XLONG; i2->src.disp = 0; i2->src.areg = i1->src.areg; i2->src.ireg = dr1; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * add.l Dm, An => move.x 0(An,Dm.l), Do * move.x (An), Do * * where An is dead */ if ((op1 == ADD) && (op2 == MOVE) && (sm1 == REG) && ISD(i1->src.areg) && (dm1 == REG) && ISA(dr1) && (i2->src.amode == REGI)&& ISA(i2->src.areg) && (i2->dst.amode == REG) && ISD(i2->dst.areg) && (dr1 == i2->src.areg) && (i1->flags & LENL)) { if ((i2->live & RM(i2->src.areg)) == 0) { i2->src.amode = REGIDX|XLONG; i2->src.disp = 0; i2->src.ireg = i1->src.areg; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } } /* * lea N(Am), An => lea N(Am,Do.l), An * add.l Do, An * */ if ((op1 == LEA) && (op2 == ADD) && (sm1 == REGID) && (i2->src.amode == REG) && ISD(i2->src.areg) && (i2->dst.amode == REG) && ISA(i2->dst.areg) && (dr1 == i2->dst.areg) && D8OK(i1->src.disp)) { i1->src.amode = REGIDX|XLONG; i1->src.ireg = i2->src.areg; delinst(bp, i2); DBG(printf("%d ", __LINE__)) return TRUE; } /* * Try to use the pre-decrement and post-increment modes * whenever possible. */ /* * sub.l #1, Am * ... stuff ... * ???.b ..(Am).. => ???.b ..-(Am).. * * Nothing in "stuff" can refer to Am. */ if ((op1 == SUB) && (i1->flags & LENL) && (sm1 == IMM) && (i1->src.disp == 1) && (dm1 == REG) && ISA(dr1)) { while (i2 != NULL) { if (i2->src.amode == REGI && i2->src.areg == dr1) { if ((i2->flags & LENB) == 0) goto end24; i2->src.amode |= DEC; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } if (i2->dst.amode == REGI && i2->dst.areg == dr1) { if ((i2->flags & LENB) == 0) goto end24; i2->dst.amode |= DEC; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } if (uses(i2, dr1)) goto end24; if (i2->next == NULL) goto end24; else i2 = i2->next; } } end24: /* * sub.l #2, Am * ... stuff ... * ???.w ..(Am).. => ???.w ..-(Am).. * * Nothing in "stuff" can refer to Am. */ if ((op1 == SUB) && (i1->flags & LENL) && (sm1 == IMM) && (i1->src.disp == 2) && (dm1 == REG) && ISA(dr1)) { while (i2 != NULL) { if (i2->src.amode == REGI && i2->src.areg == dr1) { if ((i2->flags & LENW) == 0) goto end26; i2->src.amode |= DEC; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } if (i2->dst.amode == REGI && i2->dst.areg == dr1) { if ((i2->flags & LENW) == 0) goto end26; i2->dst.amode |= DEC; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } if (uses(i2, dr1)) goto end26; if (i2->next == NULL) goto end26; else i2 = i2->next; } } end26: /* * sub.l #4, Am * ... stuff ... * ???.l ..(Am).. => ???.l ..-(Am).. * * Nothing in "stuff" can refer to Am. */ if ((op1 == SUB) && (i1->flags & LENL) && (sm1 == IMM) && (i1->src.disp == 4) && (dm1 == REG) && ISA(dr1)) { while (i2 != NULL) { if (i2->src.amode == REGI && i2->src.areg == dr1) { if ((i2->flags & LENL) == 0) goto end28; i2->src.amode |= DEC; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } if (i2->dst.amode == REGI && i2->dst.areg == dr1) { if ((i2->flags & LENL) == 0) goto end28; i2->dst.amode |= DEC; delinst(bp, i1); DBG(printf("%d ", __LINE__)) return TRUE; } if (uses(i2, dr1)) goto end28; if (i2->next == NULL) goto end28; else i2 = i2->next; } } end28: return FALSE; } /* * peep2(bp) - scan blocks starting at 'bp' */ bool peep2(bp) register BLOCK *bp; { register INST *ip; register bool changed = FALSE; DBG(printf("p2 :")) for (; bp != NULL ;bp = bp->next) { for (ip = bp->first; ip != NULL && ip->next != NULL ;) { if (ipeep2(bp, ip)) { changed = TRUE; s_peep2++; bprep(bp); /* * If we had a match, then either instruction * may have been deleted, so the safe thing to * do is to go to the next block. */ break; } else ip = ip->next; } } DBG(printf("\n"); fflush(stdout)) return changed; }