koreth@panarthea.ebay.sun.com (Steven Grimm) (10/26/89)
Submitted-by: ncar.ucar.edu!dunike!onecom!wldrdg!hans (Johann Ruegg) Posting-number: Volume 2, Issue 99 Archive-name: sozobon1.2/part08 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 8 (of 9)." # Contents: top/NPEEP2.C top/REG.C # Wrapped by koreth@panarthea on Tue Oct 24 18:40:47 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'top/NPEEP2.C' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'top/NPEEP2.C'\" else echo shar: Extracting \"'top/NPEEP2.C'\" \(17335 characters\) sed "s/^X//" >'top/NPEEP2.C' <<'END_OF_FILE' X/* Copyright (c) 1988 by Sozobon, Limited. Author: Tony Andrews X * X * Permission is granted to anyone to use this software for any purpose X * on any computer system, and to redistribute it freely, with the X * following restrictions: X * 1) No charge may be made other than reasonable charges for reproduction. X * 2) Modified versions must be clearly marked as such. X * 3) The authors are not responsible for any harmful consequences X * of using this software, even if they result from defects in it. X */ X X/* X * 2-instruction peephole optimizations X */ X X#include "top.h" X X/* X * Macros to reference commonly-used values... cleans up the following X * code quite a bit. X */ X#define sm1 i1->src.amode /* source & dest addressing modes */ X#define dm1 i1->dst.amode X#define sm2 i2->src.amode X#define dm2 i2->dst.amode X X#define sr1 i1->src.areg /* source & dest registers */ X#define dr1 i1->dst.areg X#define sr2 i2->src.areg X#define dr2 i2->dst.areg X X X/* X * ipeep2(bp, i1) - look for 2-instruction optimizations at the given inst. X */ Xstatic int Xipeep2(bp, i1) XBLOCK *bp; Xregister INST *i1; X{ X register INST *i2; /* the next instruction */ X register INST *ti2; /* "temporary" next inst */ X X register int op1, op2; /* opcodes, for speed */ X X i2 = i1->next; X op1 = i1->opcode; X op2 = i2->opcode; X X /* X * Avoid stack fix-ups after a call if possible. X */ X X /* X * addq #4,sp X * ... stuff that doesn't use SP ... X * move.l ?,-(sp) => move.l ?,(sp) X */ X if (op1 == ADDQ && sm1 == IMM && i1->src.disp == 4 && X dm1 == REG && dr1 == SP) { X X ti2 = i2; X while (!uses(ti2, SP)) { X if (ti2->next == NULL) X goto end2; X ti2 = ti2->next; X } X X if (ti2->opcode == MOVE && ti2->flags == LENL && X ti2->dst.amode == (REGI|DEC) && ti2->dst.areg == SP) { X ti2->dst.amode = REGI; X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } Xend2: X X /* X * addq #2,sp X * ... stuff that doesn't use SP ... X * move.w ?,-(sp) => move.w ?,(sp) X */ X if (op1 == ADDQ && sm1 == IMM && i1->src.disp == 2 && X dm1 == REG && dr1 == SP) { X X ti2 = i2; X while (!uses(ti2, SP)) { X if (ti2->next == NULL) X goto end3; X ti2 = ti2->next; X } X X if (ti2->opcode == MOVE && ti2->flags == LENW && X ti2->dst.amode == (REGI|DEC) && ti2->dst.areg == SP) { X ti2->dst.amode = REGI; X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } Xend3: X X /* X * Avoid "tst" instructions following instructions that X * set the Z flag. X */ X X /* X * move.x X, Y => move.x X, Y X * tst.x X or Y ...deleted... X * beq/bne beq/bne X * X * Where Y is not An, because "movea" doesn't set the X * zero flag. X */ X if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) && X op1 == MOVE && op2 == TST && X i1->flags == i2->flags) { X X /* X * If pre-decrement is set on the dest. of the move, X * don't let that screw up the operand comparison. X */ X if (dm1 & DEC) X dm1 &= ~DEC; X X if (opeq(&i1->dst, &i2->src) || opeq(&i1->src, &i2->src)) { X if (dm1 != REG || ISD(dr1)) { X delinst(bp, i2); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X } X X /* X * and.x X, Y => and.x X, Y X * tst.x X or Y ...deleted... X * beq/bne beq/bne X * X * Where Y is not An, because "movea" doesn't set the X * zero flag. X */ X if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) && X op1 == AND && op2 == TST && X i1->flags == i2->flags) { X X /* X * If pre-decrement is set on the dest. of the move, X * don't let that screw up the operand comparison. X */ X if (dm1 & DEC) X dm1 &= ~DEC; X X if (opeq(&i1->dst, &i2->src) || opeq(&i1->src, &i2->src)) { X if (dm1 != REG || ISD(dr1)) { X delinst(bp, i2); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X } X X /* X * ext.x Dn => ext.x Dn X * tst.x Dn ...deleted... X * beq/bne beq/bne X * X * Where Y is not An, because "movea" doesn't set the X * zero flag. X */ X if ((bp->last == i2) && (bp->bcode == BEQ || bp->bcode == BNE) && X (op1 == EXT) && (op2 == TST) && X (i1->flags == i2->flags)) { X X if ((sm1 == REG) && ISD(sr1) && (sm2 == REG) && (sr1 == sr2)) { X delinst(bp, i2); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * move.? X, Dn => move.? X, Dn X * ext.? Dn ...deleted... X * beq/bne beq/bne X * X * Where Dn is dead after the "ext". X */ X if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) && X op1 == MOVE && op2 == EXT) { X X if ((dm1 == REG) && ISD(dr1) && X (sm2 == REG) && (dr1 == sr2)) { X if ((i2->live & RM(sr2)) == 0) { X delinst(bp, i2); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X } X X /* X * ext.l Dm => ...deleted... X * tst.l Dm tst.w Dm X * X * where Dm is dead after the "tst". X */ X if (op1 == EXT && op2 == TST && X ((i1->flags & LENL) != 0) && ((i2->flags & LENL) != 0) && X (sr1 == sr2) && ISD(sr1)) { X X if ((i2->live & RM(sr2)) == 0) { X i2->flags = LENW; X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X#if 0 X /* X * add[q] #?,sp X * ... stuff that doesn't use SP ... X * unlk An => unlk An X */ X if ((op1 == ADDQ || op1 == ADD) && sm1 == IMM && X dm1 == REG && dr1 == SP) { X X ti2 = i2; X while (!uses(ti2, SP)) { X if (ti2->next == NULL) X goto end8; X ti2 = ti2->next; X } X if (ti2->opcode == UNLK) { X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } Xend8: X#endif X X /* X * ext.l Dm => ...deleted... X * ??? N(An,Dm.l), ?? ??? N(An,Dm.w), ?? X * X * Where Dm is dead X */ X if ((op1 == EXT) && (i1->flags & LENL) && X (sm2 == (REGIDX|XLONG)) && X (sr1 == i2->src.ireg)) { X X if ((i2->live & RM(sr1)) == 0) { X sm2 &= ~XLONG; X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * ext.l Dm => ...deleted... X * ??? ??, N(An,Dm.l) ??? ??, N(An,Dm.w) X * X * Where Dm is dead X */ X if ((op1 == EXT) && (i1->flags & LENL) && X (dm2 == (REGIDX|XLONG)) && X (sr1 == i2->dst.ireg)) { X X if ((i2->live & RM(sr1)) == 0) { X dm2 &= ~XLONG; X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * Avoid intermediate registers. X */ X X /* X * move.x X, Dm => INST.x X, Dn X * INST.x Dm, Dn X * X * where Dm is dead, and INST is one of: add, sub, and, or, cmp X */ X if ((op1 == MOVE) && X ((op2==ADD)||(op2==SUB)||(op2==AND)||(op2==OR)||(op2==CMP)) && X (i1->flags == i2->flags) && X (dm1 == REG) && ISD(dr1) && X (sm2 == REG) && ISD(sr2) && X (dm2 == REG) && ISD(dr2) && X (dr1 == sr2)) { X X if ((i2->live & RM(sr2)) == 0) { X X i1->opcode = i2->opcode; X dr1 = dr2; X X delinst(bp, i2); X DBG(printf("%d ", __LINE__)) X return DIRTY; X } X } X X /* X * Silly moves X */ X X /* X * move.x X, Y => move.x X, Y X * move.x Y, X X */ X if ((op1 == MOVE) && (op2 == MOVE) && X (i1->flags == i2->flags) && X opeq(&i1->src, &i2->dst) && opeq(&i1->dst, &i2->src) && X ((sm1 & (INC|DEC)) == 0) && X ((dm1 & (INC|DEC)) == 0)) { X X delinst(bp, i2); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X X /* X * move.x X, Y => move.x X, Rn X * move.x Y, Rn move.x Rn, Y X * X * where Y isn't INC or DEC, and isn't register direct X */ X if ((op1 == MOVE) && (op2 == MOVE) && (dm2 == REG) && X opeq(&i1->dst, &i2->src) && ((dm1 & (INC|DEC)) == 0) && X (i1->flags == i2->flags) && (dm1 != REG)) { X X freeop(&i1->dst); X i1->dst = i2->dst; X i2->dst = i2->src; X i2->src = i1->dst; X X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X X /* X * move.x Dm, X => move.x Dm, X X * move.x X, Y move.x Dm, Y X * X * Where 'x' is the same, and 'X' has no side-effects. X */ X if ((op1 == MOVE) && (op2 == MOVE) && X (sm1 == REG) && ISD(sr1) && X (i1->flags == i2->flags) && opeq(&i1->dst, &i2->src) && X ((dm1 & (DEC|INC)) == 0)) { X X X freeop(&i2->src); X i2->src = i1->src; X DBG(printf("%d ", __LINE__)) X return DIRTY; X } X X#if 0 X /* X * move.? Rm, Rn => move.? Rm, Rn X * ... stuff ... ... stuff ... X * move.? Rm, Rn X * X * where "stuff" doesn't set Rm or Rn. Also make sure a X * conditional branch doesn't directly follow the second X * move. If so, we need to leave the move alone since it X * sets condition codes. X */ X if ((op1 == MOVE) && (sm1 == REG) && (dm1 == REG)) { X ti2 = i2; X while (ti2 != NULL && !sets(ti2, sr1) && !sets(ti2, dr1)) { X X if ((ti2->opcode==MOVE) && (i1->flags==ti2->flags) && X (ti2->src.amode==REG) && (ti2->dst.amode==REG) && X (sr1 == ti2->src.areg) && X (dr1 == ti2->dst.areg)) { X X delinst(bp, ti2); X DBG(printf("%d ", __LINE__)) X return DIRTY; X } X ti2 = ti2->next; X } X } X#endif X X /* X * move.l Am, Dn => move.l Am, Ao X * ... stuff ... ... stuff ... X * move.l Dn, Ao X * X * where "stuff" doesn't set Dn. X */ X if ((op1 == MOVE) && (i1->flags == LENL) && X (sm1 == REG) && ISA(sr1) && (dm1 == REG) && ISD(dr1)) { X X ti2 = i2; X while (!sets(ti2, dr1)) { X X if ((ti2->opcode == MOVE) && (ti2->flags == LENL) && X (ti2->src.amode == REG) && ISD(ti2->src.areg) && X (ti2->dst.amode == REG) && ISA(ti2->dst.areg) && X (dr1 == ti2->src.areg)) { X X /* X * If the intermediate register isn't dead, X * then we have to keep using it. X */ X if ((ti2->live & RM(ti2->src.areg)) != 0) X goto end14; X X dr1 = ti2->dst.areg; X X delinst(bp, ti2); X DBG(printf("%d ", __LINE__)) X return DIRTY; X } X X if (ti2->next == NULL) X goto end14; X X ti2 = ti2->next; X } X } Xend14: X X /* X * move.l Dm, An => move.l Dm, Ao X * lea (An), Ao X * X * where An is dead X */ X if ((op1 == MOVE) && (op2 == LEA) && (sm1 == REG) && ISD(sr1) && X (dm1 == REG) && ISA(dr1) && (sm2 == REGI) && (dm2 == REG) && X ISA(dr2) && (dr1 == sr2)) { X X if ((i2->live & RM(sr2)) == 0) { X X dr1 = dr2; X i1->live = i2->live; X X delinst(bp, i2); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * lea X, An => lea X, Ao X * lea (An), Ao X * X * where An is dead X */ X if ((op1 == LEA) && (op2 == LEA) && (sm2 == REGI) && (dr1 == sr2)) { X X if ((i2->live & RM(sr2)) == 0) { X X dr1 = dr2; X i1->live = i2->live; X X delinst(bp, i2); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * lea N(Am), Am => X * ? (Am)[,...] ? N(Am)[,...] X * X * Where Am is either dead after the second instruction or X * is a direct destination of the second instruction. X */ X if ((op1 == LEA) && (sm1 == REGID) && (sr1 == dr1) && X (sm2 == REGI) && (dr1 == sr2)) { X X if (((i2->live & RM(sr2)) == 0) || X ((dm2 == REG) && (dr2 == sr2))) { X i2->src.amode = REGID; X i2->src.disp = i1->src.disp; X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return DIRTY; X } X } X X /* X * lea N(Am), Am => X * ? X, (Am) ? X, N(Am) X * X * Where X doesn't reference Am, and Am is dead after the X * second instruction. X */ X if ((op1 == LEA) && (sm1 == REGID) && X (sr1 == dr1) && (dm2 == REGI) && (dr1 == dr2)) { X X if (((i2->live & RM(dr2)) == 0) && X ((sm2 == IMM) || (sm2 == ABS) || (sr2 != dr2))) { X dm2 = REGID; X i2->dst.disp = i1->src.disp; X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return DIRTY; X } X } X X /* X * lea X, Am => ...deleted... X * clr.x (Am) clr.x X X * X * where Am is dead X */ X if ((op1 == LEA) && (op2 == CLR) && (sm2 == REGI) && (dr1 == sr2)) { X X if ((i2->live & RM(sr2)) == 0) { X i2->src = i1->src; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * lea X, Am => ...deleted... X * move.x Y, (Am) move.x Y, X X * X * where Am is dead X */ X if ((op1 == LEA) && (op2 == MOVE) && dm2 == REGI && (dr1 == dr2)) { X X if ((i2->live & RM(dr2)) == 0) { X i2->dst = i1->src; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * lea X, Am => ...deleted... X * move.x (Am), Y move.x X, Y X * X * where Am is dead X */ X if ((op1 == LEA) && (op2 == MOVE) && sm2 == REGI && (dr1 == sr2)) { X X if ((i2->live & RM(sr2)) == 0) { X i2->src = i1->src; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * move.x Dm, X => move.x Dm, X X * cmp.x #N, X cmp.x #N, Dm X * X * Where X isn't register direct. X * X * Since X generally references memory, we can compare X * with the register faster. X */ X if ((op1 == MOVE) && (op2 == CMP) && X (i1->flags == i2->flags) && (sm2 == IMM) && X (sm1 == REG) && ISD(sr1) && (dm1 != REG) && X opeq(&i1->dst, &i2->dst) && ((dm1 & (INC|DEC)) == 0)) { X X freeop(&i2->dst); X dm2 = REG; X dr2 = sr1; X X DBG(printf("%d ", __LINE__)) X return DIRTY; X } X X X /* X * Try to use register indirect w/ displacement and/or index X */ X X /* X * add.l Am, Dn => lea 0(Am,Dn.l), Ao X * move.l Dn, Ao X * X * where Dn is dead X */ X if ((op1 == ADD) && (op2 == MOVE) && X (sm1 == REG) && ISA(sr1) && X (dm1 == REG) && ISD(dr1) && X (sm2 == REG) && ISD(sr2) && X (dm2 == REG) && ISA(dr2) && X (dr1 == sr2) && X (i1->flags & LENL) && (i2->flags & LENL)) { X X if ((i2->live & RM(sr2)) == 0) { X X i2->opcode = LEA; X i2->flags = 0; X X sm2 = REGIDX|XLONG; X i2->src.disp = 0; X sr2 = sr1; X i2->src.ireg = dr1; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * add.l Dm, An => move.x 0(An,Dm.l), Do X * move.x (An), Do X * X * where An is dead X */ X if ((op1 == ADD) && (op2 == MOVE) && X (sm1 == REG) && ISD(i1->src.areg) && X (dm1 == REG) && ISA(dr1) && X (sm2 == REGI)&& ISA(sr2) && X (dm2 == REG) && ISD(dr2) && X (dr1 == sr2) && (i1->flags & LENL)) { X X if ((i2->live & RM(sr2)) == 0) { X X sm2 = REGIDX|XLONG; X i2->src.disp = 0; X i2->src.ireg = sr1; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X } X X /* X * lea N(Am), An => lea N(Am,Do.l), An X * add.l Do, An X * X */ X if ((op1 == LEA) && (op2 == ADD) && (sm1 == REGID) && X (sm2 == REG) && ISD(sr2) && (dm2 == REG) && ISA(dr2) && X (dr1 == dr2) && D8OK(i1->src.disp)) { X X sm1 = REGIDX|XLONG; X i1->src.ireg = sr2; X i1->live = i2->live; X delinst(bp, i2); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X X X X /* X * Try to use the pre-decrement and post-increment modes X * whenever possible. X */ X X /* X * sub.l #1, Am X * ... stuff ... X * ???.b ..(Am).. => ???.b ..-(Am).. X * X * Nothing in "stuff" can refer to Am. X */ X if ((op1 == SUBQ) && (i1->flags & LENL) && X (sm1 == IMM) && (i1->src.disp == 1) && X (dm1 == REG) && ISA(dr1)) { X X while (i2 != NULL) { X X if (sm2 == REGI && sr2 == dr1) { X X if ((i2->flags & LENB) == 0) X goto end24; X X sm2 |= DEC; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return DIRTY; X } X if (dm2 == REGI && dr2 == dr1) { X X if ((i2->flags & LENB) == 0) X goto end24; X X dm2 |= DEC; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X X if (uses(i2, RM(dr1))) X goto end24; X X if (i2->next == NULL) X goto end24; X else X i2 = i2->next; X X } X } Xend24: X X /* X * sub.l #2, Am X * ... stuff ... X * ???.w ..(Am).. => ???.w ..-(Am).. X * X * Nothing in "stuff" can refer to Am. X */ X if ((op1 == SUBQ) && (i1->flags & LENL) && X (sm1 == IMM) && (i1->src.disp == 2) && X (dm1 == REG) && ISA(dr1)) { X X while (i2 != NULL) { X X if (sm2 == REGI && sr2 == dr1) { X X if ((i2->flags & LENW) == 0) X goto end26; X X sm2 |= DEC; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X if (dm2 == REGI && dr2 == dr1) { X X if ((i2->flags & LENW) == 0) X goto end26; X X dm2 |= DEC; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X X if (uses(i2, RM(dr1))) X goto end26; X X if (i2->next == NULL) X goto end26; X else X i2 = i2->next; X X } X } Xend26: X X /* X * sub.l #4, Am X * ... stuff ... X * ???.l ..(Am).. => ???.l ..-(Am).. X * X * Nothing in "stuff" can refer to Am. X */ X if ((op1 == SUBQ) && (i1->flags & LENL) && X (sm1 == IMM) && (i1->src.disp == 4) && X (dm1 == REG) && ISA(dr1)) { X X while (i2 != NULL) { X X if (sm2 == REGI && sr2 == dr1) { X X if ((i2->flags & LENL) == 0) X goto end28; X X sm2 |= DEC; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X if (dm2 == REGI && dr2 == dr1) { X X if ((i2->flags & LENL) == 0) X goto end28; X X i2->dst.amode |= DEC; X X delinst(bp, i1); X DBG(printf("%d ", __LINE__)) X return CLEAN; X } X X if (uses(i2, RM(dr1))) X goto end28; X X if (i2->next == NULL) X goto end28; X else X i2 = i2->next; X X } X } Xend28: X X return UNCHANGED; X} X X/* X * peep2(bp) - scan blocks starting at 'bp' X */ Xint Xpeep2(bp) Xregister BLOCK *bp; X{ X register INST *ip; X register bool i; X register int state = UNCHANGED; X X DBG(printf("p2 :")) X X for (; bp != NULL ;bp = bp->next) { X for (ip = bp->first; ip != NULL && ip->next != NULL ;) { X if ((i = ipeep2(bp, ip)) != UNCHANGED) { X X if (i == DIRTY) X state = DIRTY; X if (state == UNCHANGED) X state = CLEAN; X X s_peep2++; X /* X * If we had a match, then either instruction X * may have been deleted, so the safe thing to X * do is to go to the next block. X */ X break; X } else X ip = ip->next; X } X } X DBG(printf("\n"); fflush(stdout)) X return state; X} END_OF_FILE if test 17335 -ne `wc -c <'top/NPEEP2.C'`; then echo shar: \"'top/NPEEP2.C'\" unpacked with wrong size! fi # end of 'top/NPEEP2.C' fi if test -f 'top/REG.C' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'top/REG.C'\" else echo shar: Extracting \"'top/REG.C'\" \(17767 characters\) sed "s/^X//" >'top/REG.C' <<'END_OF_FILE' X/* Copyright (c) 1989 by Sozobon, Limited. Author: Tony Andrews X * X * Permission is granted to anyone to use this software for any purpose X * on any computer system, and to redistribute it freely, with the X * following restrictions: X * 1) No charge may be made other than reasonable charges for reproduction. X * 2) Modified versions must be clearly marked as such. X * 3) The authors are not responsible for any harmful consequences X * of using this software, even if they result from defects in it. X */ X X/* X * The code in this file deals with "registerizing" local variables and X * parameters. The general idea is to look for highly referenced local X * variables and parameters and effectively turn them into register X * variables automatically. Only the D registers are used, currently, so X * for pointer variables, a manual "register" declaration in the source X * code is actually better. X * X * We need to be certain of several things about a variable before placing X * it in a register. It's address must not be taken, and it must not be X * referred to through "aliases" (e.g. when casting to a shorter object). X * It must be able to fit in a register. And to keep things like printf from X * breaking, parameters can only be registerized if none of the parameters X * have their address taken. X * X * The compiler makes this all possible by placing "hints" within the X * generated assembly code. These hints appear as comments, but are parsed X * by the optimizer, and the information is stashed away by calling addvar(). X * The hints give us the size and offset of each parameter and local variable. X * Their names are also given, although that information isn't needed here. X * X * There are tradeoffs to be wary of when registerizing. If no register X * variables exist yet, then "movem" instructions have to be added, requiring X * more references to make this worthwhile. In the case of parameters, the X * register has to be initialized from the stack. The four cases are: X * X * Locals w/ other regs: 1 reference required X * no other regs: 4 references required X * Parms w/ other regs: 2 references required X * no other regs: 6 references required X * X * The numbers above represent the break-even point based on a savings of X * 2 bytes per reference, and the incremental cost of adding "movem" or X * "move" instructions as needed. X * X * This optimizes for space only. To optimize for time, each reference would X * be weighted based on the loop nesting level at which it occurs. X */ X X#include "top.h" X X#define MAXLOCALS 100 X Xstatic struct linfo { X long offset; /* offset from A6 */ X int size; /* size of the object */ X int ref; /* # of references to the local */ X int reg; /* reg. we assigned it to */ X int flags; /* length, etc. */ X} locals[MAXLOCALS]; X X#define ALIASED 0x1 /* offset is aliased with another */ X#define ADDR_TAKEN 0x2 /* address of the variable was taken */ X X#define IS_LOCAL(x) (locals[(x)].offset < 0) X#define IS_PARM(x) (locals[(x)].offset > 0) X Xstatic bool paddr; /* address of a parameter was taken */ Xstatic int lcnt; /* number of local variables we've seen */ Xstatic int rcnt; /* number of locals that got registerized */ X Xstatic int omask, nmask; /* old and new register masks */ X X/* X * addvar(size, off) - add a variable entry for the current function X * X * These come from hints the compiler gives us about local variables. X * We use the size and offset here to make sure we don't have aliasing X * problems with the local variables we want to registerize. X */ Xvoid Xaddvar(size, off) Xint size; Xint off; X{ X locals[lcnt].offset = off; X locals[lcnt].size = size; X locals[lcnt].flags = 0; X locals[lcnt].ref = 0; X X lcnt++; X} X X/* X * clrvar() - clear the variable list X */ Xvoid Xclrvar() X{ X register int i; X X /* X * re-initialize the local information X */ X for (i=0; i < MAXLOCALS ;i++) { X locals[i].ref = 0; X locals[i].reg = -1; X locals[i].flags = 0; X locals[i].offset = 0; X locals[i].size = 0; X } X paddr = FALSE; X rcnt = lcnt = 0; X} X X/* X * setreg() - try to "registerize" local variables in the given function X */ Xvoid Xsetreg(bp) XBLOCK *bp; X{ X void lcheck(), lassign(), lrewrite(); X X lcheck(bp); X lassign(); X X#ifdef DEBUG X if (debug) X dump_table(); X#endif X X if (rcnt > 0) X lrewrite(bp); X X s_reg += rcnt; /* keep totals for accounting */ X} X X/* X * lcheck() - scan for local variable references in the given function X */ Xstatic void Xlcheck(bp) XBLOCK *bp; X{ X void ckref(); X register int i; X register BLOCK *cb; X register INST *ci; X X for (cb = bp; cb != NULL ;cb = cb->next) { X for (ci = cb->first; ci != NULL ;ci = ci->next) { X ckref(ci, &ci->src); X ckref(ci, &ci->dst); X } X } X X /* X * Now figure out which registers are currently used. X */ X ci = bp->first->next; X X if (ci != NULL && ci->opcode == MOVEM) { X if (ci->src.amode == REG) X omask = RM(ci->src.areg); X else X omask = stomask(ci->src.astr); X } else X omask = 0; X} X X/* X * ckref() - check for a local variable reference X * X * If a local variable reference is found, it's added to the table or X * (if already there) its reference count is incremented. If we're X * taking its address, note that too. X */ Xstatic void Xckref(ip, op) XINST *ip; Xstruct opnd *op; X{ X register int i; X register int sz; X X if (op->amode != REGID || op->areg != A6) X return; X X switch (ip->flags) { X case LENL: X sz = 4; X break; X case LENW: X sz = 2; X break; X case LENB: X default: /* for LEA and PEA */ X sz = 1; X break; X } X X /* X * is the local variable already in the table? X */ X for (i=0; i < lcnt ;i++) { X if (locals[i].offset == op->disp && locals[i].size == sz) { X locals[i].ref++; X break; X } X } X X /* X * If not in the table, add an entry for it. If we add an entry X * here, it must be an alias for one of the entries we got via X * the compiler hints. X */ X if (i == lcnt) { X locals[lcnt].offset = op->disp; X locals[lcnt].size = sz; X locals[lcnt].flags = 0; X locals[lcnt].ref = 1; X X lcnt++; X } X X if (ip->opcode == LEA || ip->opcode == PEA) { X locals[i].flags = ADDR_TAKEN; X /* X * If we took the address of a parameter, note that X * by setting 'paddr'. X */ X if (IS_PARM(i)) X paddr = TRUE; X } X} X X/* X * lassign() - assign local variable to registers X * X * Check for aliases, sort the table, and then decide how to assign X * the local variables to registers. X */ Xstatic void Xlassign() X{ X void ck_aliases(), sort_table(), do_sort(); X register int i; X register int r; X int minlref; /* min. required references for a local */ X int minpref; /* min. required references for a parameter */ X X ck_aliases(); /* disqualify any "aliased" references */ X sort_table(); /* and sort by reference count */ X X /* X * If there were already "movem" instructions, then we should X * convert as many locals as possible to registers. If we're X * going to have to add the movem's, then we need at least 4 X * references for this to be worthwhile. The 2 movem instructions X * take 8 bytes, and each reference conversion saves 2 bytes. X * This analysis optimizes for size. X */ X minlref = (omask != 0) ? 1 : 4; X minpref = (omask != 0) ? 2 : 6; X X nmask = omask; X X for (i=0, r=D3; r <= D7 ;) { X X /* X * If the register is already in use, skip it. X */ X if (omask & RM(r)) { X r++; X continue; X } X X /* X * If no more eligible variables, then stop. X */ X if (locals[i].ref <= 0) X break; X X /* X * If something meets the minimums, then assign it to X * the current register, and adjust the minimums. X */ X if ((IS_LOCAL(i) && locals[i].ref >= minlref) || X (IS_PARM(i) && locals[i].ref >= minpref)) { X locals[i].reg = r; X nmask |= RM(r); X minlref = 1; X minpref = 2; X r++; X i++; X } else { X /* X * If we run into something that isn't referenced X * enough, disqualify it and re-sort. There might X * still be something else worth doing. X */ X locals[i].ref = -locals[i].ref; X do_sort(); X } X } X rcnt = i; X} X X/* X * ck_aliases() - check for aliases in the locals table X * X * An alias occurs when two different offsets off of A6 both reference X * the same local. This can happen when casting to a smaller type. Since X * these references would be a pain to rewrite, we just bag it. X */ Xstatic void Xck_aliases() X{ X static bool ck_aref(); X register int i; X register int s; X register long d; X X for (i=0; i < lcnt ;i++) { X d = locals[i].offset; X s = locals[i].size; X X if (ck_aref(d, s)) X locals[i].flags |= ALIASED; X } X} X X/* X * ck_aref() - check for an aliased reference X */ Xstatic bool Xck_aref(d, len) Xregister long d; Xregister int len; X{ X register int i; X X for (i=0; i < lcnt ;i++) { X if (locals[i].offset == d && locals[i].size == len) X continue; X X if (overlaps(d, len, locals[i].offset, locals[i].size)) { X locals[i].flags |= ALIASED; X return TRUE; X } X } X return FALSE; X} X Xstatic bool Xoverlaps(d1, s1, d2, s2) Xregister long d1, d2; Xint s1, s2; X{ X register long e1, e2; X X e1 = d1 + s1 - 1; X e2 = d2 + s2 - 1; X X if (d1 >= d2 && d1 <= e2) /* d1 inside d2 <=> e2 */ X return TRUE; X X if (e1 >= d2 && e1 <= e2) /* e1 inside d2 <=> e2 */ X return TRUE; X X return FALSE; X} X Xstatic void Xsort_table() X{ X register int i; X X /* X * Remove uninteresting references from consideration: X * X * 1. Variables whose address was taken, or are aliased with another. X * 2. Variables that don't fit in a register. X */ X for (i=0; i < lcnt ;i++) { X if (locals[i].flags&(ADDR_TAKEN|ALIASED) || locals[i].size > 4) X locals[i].ref = -locals[i].ref; X } X X /* X * If paddr is set, remove any parameters from consideration. We X * have to do this so that things like printf (that take the address X * of a parameter and increment it) don't break. Only if no parameter X * addresses are taken, can we consider registerizing any of them. X */ X if (paddr) { X for (i=0; i < lcnt ;i++) { X if (IS_PARM(i) && (locals[i].ref > 0)) X locals[i].ref = -locals[i].ref; X } X } X X do_sort(); X} X Xstatic void Xdo_sort() X{ X register int i; X struct linfo l; X X /* X * simple bubble sort X */ X for (i=0; i < (lcnt-1) ;) { X if (locals[i].ref < locals[i+1].ref) { X l = locals[i]; X locals[i] = locals[i+1]; X locals[i+1] = l; X if (i > 0) X i--; X } else X i++; X } X} X X/* X * lrewrite() - rewrite the function based on the new register assignments X * X * Fixing the references is easy, but we have to fix up (or add) the movem X * instructions as well. Also, we call addinits() to initialize any registers X * that will contain parameters. X */ Xstatic void Xlrewrite(bp) XBLOCK *bp; X{ X void fixref(), fixmove(), addmovem(); X INST *findlnk(); X register int i; X register BLOCK *cb; X register INST *ci; X X /* X * First, rewrite all the references to the locals that X * we've reassigned to registers. X */ X for (cb = bp; cb != NULL ;cb = cb->next) { X for (ci = cb->first; ci != NULL ;ci = ci->next) { X fixref(&ci->src); X fixref(&ci->dst); X } X } X X /* X * If the movem's are there, just find them and fix up the X * register specs. X */ X ci = bp->first->next; X if (ci != NULL && ci->opcode == MOVEM) { X X /* X * First, add the initialization instructions. X */ X addinits(bp, bp->first->next); X X fixmove(&ci->src); X X for (cb = bp; cb != NULL ;cb = cb->next) { X if (cb->flags & B_RET) { X for (ci=cb->last; ci != NULL ;ci=ci->prev) { X if (ci->opcode == MOVEM) { X fixmove(&ci->dst); X return; X } X } X } X } X return; X } X X#ifdef DEBUG X if (debug) X printf("adding movem instructions\n"); X#endif X /* X * There aren't any movem instructions, so we have to add X * them here. What a pain... X */ X addmovem(bp, findlnk(bp), TRUE); X addinits(bp, findlnk(bp)->next); X X for (cb = bp; cb != NULL ;cb = cb->next) { X if (cb->last->opcode == RTS) { X for (ci=cb->last; ci != NULL ;ci=ci->prev) { X if (ci->opcode == UNLK) { X addmovem(cb, ci, FALSE); X return; X } X } X } X } X /* X * Reaching this point would be an error, you'd think. It means X * we didn't find the exit from this function. Strangely enough, X * this can actually happen in routines with infinite loops. X * Since the "return" block isn't reachable, branch optimization X * actually removes it. So we can't consider it an error here if X * we don't find any "unlk" instruction. X */ X} X Xstatic void Xfixmove(op) Xstruct opnd *op; X{ X char *masktos(); X X freeop(op); X op->amode = ABS; X op->astr = strsave(masktos(nmask)); X} X X/* X * findlnk() - find the LINK instruction in the given block X * X * When profiling, the LINK isn't the first instruction in the entry X * block. This function lets us handle both cases cleanly. X */ Xstatic INST * Xfindlnk(bp) XBLOCK *bp; X{ X INST *ip; X X for (ip=bp->first; ip != NULL ;ip = ip->next) { X if (ip->opcode == LINK) X return ip; X } X return NULL; X} X Xstatic void Xaddmovem(bp, ip, is_entry) XBLOCK *bp; /* block where we're working */ XINST *ip; /* instruction before or after the movem */ Xbool is_entry; /* true if we're doing the entry code */ X{ X char *masktos(); X register INST *ni; X struct opnd *op; X register int i; X X if (ip == NULL) /* no LINK found */ X return; X X /* X * Allocate and initialize a new instruction X */ X ni = (INST *) alloc(sizeof(INST)); X X ni->flags = LENL; X ni->opcode = MOVEM; X ni->live = 0; X ni->rref = ni->rset = 0; X X ni->src.areg = ni->dst.areg = 0; X ni->src.ireg = ni->dst.ireg = 0; X ni->src.disp = ni->dst.disp = 0; X X /* X * Set up the SP reference X */ X op = (is_entry) ? &ni->dst : &ni->src; X op->amode = (is_entry) ? REGI|DEC : REGI|INC; X op->areg = SP; X X /* X * Set up the register spec operand X */ X op = (is_entry) ? &ni->src : &ni->dst; X X op->amode = ABS; X op->astr = strsave(masktos(nmask)); X X /* X * If there's only one register being used, we should really X * change the operand to be register direct. This way, the X * peephole optimization will turn the "movem" into a simple X * "move". Since we're adding an instruction here, we really X * need to make it as painless as possible. X */ X if (rcnt == 1) { X free(op->astr); X op->amode = REG; X X for (i=D0; i <= D7 ;i++) { X if (nmask & RM(i)) { X op->areg = i; X break; X } X } X } X X /* X * Link the instruction into the block X */ X if (is_entry) { X ni->next = ip->next; /* link the MOVEM to its neighbors */ X ni->prev = ip; X X ip->next = ni; /* link its neighbors to the MOVEM */ X X if (bp->last == ip) X bp->last = ni; X else X ni->next->prev = ni; X } else { X ni->next = ip; /* link the MOVEM to its neighbors */ X ni->prev = ip->prev; X X ip->prev = ni; /* link its neighbors to the MOVEM */ X X if (bp->first == ip) X bp->first = ni; X else X ni->prev->next = ni; X } X} X Xstatic void Xaddinits(bp, ip) XBLOCK *bp; /* block where we're working */ XINST *ip; /* instruction before the moves */ X{ X char *masktos(); X register INST *ni; X struct opnd *op; X register int i; X X if (ip == NULL) /* no LINK found */ X return; X X for (i=0; i < rcnt ;i++) { X /* X * If it's a local variable, we don't have to do anything. X */ X if (IS_LOCAL(i)) X continue; X X /* X * Allocate and initialize a new instruction X */ X ni = (INST *) alloc(sizeof(INST)); X X switch (locals[i].size) { X case 1: X ni->flags = LENB; X break; X case 2: X ni->flags = LENW; X break; X case 4: X ni->flags = LENL; X break; X default: X fprintf(stderr, "Invalid length\n"); X exit(1); X } X X ni->opcode = MOVE; X ni->live = 0; X ni->rref = ni->rset = 0; X ni->src.ireg = ni->dst.ireg = 0; X X /* X * Set up the variable reference. X */ X ni->src.amode = REGID; X ni->src.areg = A6; X ni->src.disp = locals[i].offset; X X /* X * Set up the register spec operand X */ X ni->dst.amode = REG; X ni->dst.areg = locals[i].reg; X ni->dst.disp = 0; X X /* X * Link the instruction into the block X */ X ni->next = ip->next; /* link MOVE to its neighbors */ X ni->prev = ip; X X ip->next = ni; /* link neighbors to the MOVE */ X X if (bp->last == ip) X bp->last = ni; X else X ni->next->prev = ni; X } X} X Xstatic void Xfixref(op) Xstruct opnd *op; X{ X register int i; X X if (op->amode != REGID || op->areg != A6) X return; X X /* X * Does the reference need to be changed? X */ X for (i=0; i < rcnt ;i++) { X if (locals[i].offset == op->disp) { X op->amode = REG; X op->areg = locals[i].reg; X return; X } X } X} X X/* X * stomask() - convert a register list to a mask X * X * Convert a string like "Rm-Rn/Ro-Rp" or "Rm-Rn" to the appropriate X * mask value. X */ Xstatic int Xstomask(s) Xchar *s; X{ X register int mask; X register char *p; X X mask = dorspec(s); X X for (p=s; *p && *p != '/' ;p++) X ; X X if (*p == '/') X mask |= dorspec(p+1); X X return mask; X} X X/* X * dorspec() - convert a partial register spec X * X * Convert a string like "Rm" or "Rm-Rn" to a mask. X */ Xstatic int Xdorspec(s) Xregister char *s; X{ X register int base; X register int m, n; X register int mask; X X base = (s[0] == 'd') ? D0 : A0; X X m = s[1] - '0' + base; X X if (s[2] != '-') X return RM(m); X X n = s[4] - '0' + base; X X for (mask=0; m <= n ;m++) X mask |= RM(m); X X return mask; X} X X/* X * masktos() - convert a register mask to a descriptive string X * X * Generates a string of the form "Rm/Rn/Ro/..." X */ Xstatic char * Xmasktos(mask) Xregister int mask; X{ X static char buf[64]; X register char *p = buf; X register int r; X X for (r = D0; r <= D7 ;r++) { X if (mask & RM(r)) { X if (p != buf) X *p++ = '/'; X *p++ = 'd'; X *p++ = (r - D0) + '0'; X } X } X for (r = A0; r <= A7 ;r++) { X if (mask & RM(r)) { X if (p != buf) X *p++ = '/'; X *p++ = 'a'; X *p++ = (r - A0) + '0'; X } X } X *p = '\0'; X X return buf; X} X X#ifdef DEBUG Xdump_table() X{ X register int i; X X printf("%d local variables and parameters found\n", lcnt); X for (i=0; i < lcnt ;i++) { X printf("len = %d\n", locals[i].size); X printf("%02d: disp=%3ld, len=%d ref=%2d reg=%s", X i, locals[i].offset, X locals[i].size, X locals[i].ref, X locals[i].reg >= 0 ? masktos(RM(locals[i].reg)) : "-"); X if (locals[i].flags & ADDR_TAKEN) X printf(" ADDR_TAKEN"); X if (locals[i].flags & ALIASED) X printf(" ALIASED"); X printf("\n"); X } X} X#endif END_OF_FILE if test 17767 -ne `wc -c <'top/REG.C'`; then echo shar: \"'top/REG.C'\" unpacked with wrong size! fi # end of 'top/REG.C' fi echo shar: End of archive 8 \(of 9\). cp /dev/null ark8isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 9 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0