Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
c/old/kcc/ccopt.c
There are 8 other files named ccopt.c in the archive. Click here to see a list.
/*
** ccopt - miscellaneous peephole optimizations for KCC
** (C) 1981 K. Chen
**
** This collects the peephole optimizations that have not been
** classified into other files, and which are not still integrated
** with generation of code into the peephole buffer (in cccode) or
** emission of it from there into the assembly language output file
** (in ccout).
**
** Other modules containing peephole optimization routines include:
** cccreg - propagation of MOVE R,S back through peephole buffer
** cccse - finding registers already containing calculated value
** ccjskp - rearrangement of jump and skip instructions
*/
#include "ccgen.h"
#include "ccsym.h"
#include "cccreg.h"
extern pcode before(), after();
/*
** Make sure previous points to something.
**
** This should be called after a pseudo-code which might be the last
** in the buffer is NOPed out. It sets previous to the new last
** instruction in the peephole buffer.
**
** Someday this might also change maxcode to save buffer space.
*/
void fixprev()
{
if (previous != NULL && previous->pop == NOP) previous = before (previous);
}
/*
** Form local byte pointer.
**
** localbyte (op, r, s)
** assumes that a byte pointer has been calculated into register S,
** and forms the given instruction from that byte pointer into register
** R as per code0. The byte pointer will not be saved, so it is
** safe to make it a literal local if that is useful. The return value
** is 1 if the op could be folded into the previous instructions,
** and 0 otherwise (in which case it still needs to be emitted).
*/
int localbyte(op, r, s)
{
int b;
pcode q;
/* local byte pointer copy of $BYTE */
static bytepos[] = { 0331100, 0221100, 0111100, 0001100,
0350700, 0260700, 0170700, 0100700, 0010700 };
if (previous->ptype == MINDEXED && previous->pptr == &bytsym &&
previous->preg == s /* && !prevskips(previous) checked with ptype */) {
/*
** fold: IOR I,$BYTE+n
** DPB R,I
**
** into: DPB R,[ppssii,,0]
**
** and perhaps fold further with previous ops...
*/
b = bytepos[previous->poffset]; /* get offset */
previous->pop = NOP; /* drop opcode */
fixprev(); /* from peephole buffer */
code2(op, r, b, s, NULL, 0); /* make new op */
return 1; /* folded, say so */
} else if (previous->pop == ADJBP && previous->ptype == REGIS &&
(q = before(previous)) != NULL && q->pptr == &bytsym &&
q->preg == previous->pr2 && !prevskips(q)) {
/*
** fold: IOR I,$BYTE+n
** ADJBP S,I
** DPB R,S
**
** into: ADJSP S,[ppssii,,0]
** DPB R,S
*/
r = previous->preg; /* pick up register */
b = bytepos[q->poffset]; /* byte pointer part */
s = q->preg; /* and index reg */
q->pop = NOP; /* drop IOR $BYTE */
previous->pop = NOP; /* and ADJBP */
fixprev();
code2(ADJBP, r, b, s, NULL, 0); /* make new op, but still fail */
}
return 0;
}
/*
** Push AOS up into the loop it comes from.
**
** Often a for(;;) loop will have an AOS (and possibly a test) at the bottom.
** The variable is likely to be used in the body of the loop, so for simple
** loops we can merge the AOS in with the previous MOVE of the same variable
** (sort of like a reverse common subexpression elimination).
*/
void foldinc(p)
pcode p;
{
int safeindex[NREGS], i;
pcode q, b;
if (prevskips (p) || !optimize) return;
for (i = 0; i < NREGS; i++) safeindex[i] = 1;
safeindex[p->pindex] = 0; /* but the preg is safe */
switch (p->pop) {
case AOS:
i = -1;
break;
case SOS:
i = 1;
break;
default:
return;
}
q = p;
while ((q = before (q)) != NULL) switch (q->ptype & ADRMODE) {
case REGIS:
safeindex[q->pr1] = 0;
safeindex[q->pr2] = 0;
switch (q->pop) {
case SPUSH: case SPOP:
return;
case DFAD: case DFSB: case DFMP: case DFDV:
case DMOVE: case DMOVN: case IDIV: case DFLOT:
case DFIX:
safeindex[q->pr1 + 1] = 0;
safeindex[q->pr2 + 1] = 0;
}
break;
case RCONST:
case ONEREG:
case PFLOAT:
safeindex[q->preg] = 0;
break;
case BYTEPOINT:
safeindex[q->preg] = 0;
safeindex[q->pindex & REGMASK] = 0;
break;
case MINDEXED:
switch (q->pop) {
case MOVE:
if (sameaddr (p, q, 0)) { /* found what we want? */
if (prevskips (q) || !safeindex[q->preg]) return;
q->pop = p->pop;
b = q;
do {
b = after (b);
if ((b->ptype&ADRMODE) == MINDEXED && b->pindex == q->preg)
b->poffset += i;
else if (b->ptype == IMMED && b->preg == q->preg &&
b->pop == IMUL) i *= b->pvalue;
} while (b != p);
i = p->preg;
p->pop = NOP; /* drop extra AOS */
fixprev();
code0 (MOVE, i, q->preg);
return;
}
case DFAD: case DFSB: case DFMP: case DFDV:
case DMOVE: case DMOVN: case IDIV: case DFLOT:
case DFIX:
safeindex [q->preg + 1] = 0;
case FADR: case FSBR: case FMPR: case FDVR:
case ADD: case IMUL: case SUB: case AND:
case IOR: case XOR: case ADJBP: case LSH:
case SETCM: case MOVN: case SETO: case SETZ:
case HRRZ: case HLRZ: case FIXR: case FLTR:
case LDB:
if (q->preg == p->pindex || sameaddr (p, q, 0)) return;
safeindex [q->preg] = 0;
continue;
case DPB: case ILDB: case IDPB: case IBP:
case FADR+BOTH: case FSBR+BOTH: case FMPR+BOTH: case FDVR+BOTH:
case MOVEM: case ADD+BOTH: case IMUL+BOTH: case AND+BOTH:
case XOR+BOTH: case IOR+BOTH: case AOS: case SOS:
case HRRM: case HRLM: case MOVN+BOTH: case MOVE+BOTH:
case SETZ+BOTH: case SETO+BOTH: case SETCM+BOTH:
if (q->preg == p->pindex || sameaddr (p, q, 0) || alias (p, q))
return;
safeindex [q->preg] = 0;
continue;
}
default: /* some op type we don't know? */
return;
}
}
/*
** Improve instruction used to do addition.
**
** foldplus (p)
** takes an addition operation (ADD, ADDB, etc) in the peephole buffer
** and folds it in with previous instructions, as well as just improving
** it in itself.
*/
void foldplus(p)
pcode p;
{
pcode q, b;
int r;
if (p->pop == ADD && p->ptype == REGIS /* !prevskips */ &&
(q = before(p)) != NULL && q->preg == p->pr2 && !prevskips (q))
switch (q->pop) {
case SUB:
if (q->ptype == IMMED) {
q->pop = ADD; /* make SUBI R,i into ADDI R,-i */
q->pvalue = - q->pvalue; /* so it can be folded later */
} /* in any case treat like ADD here */
case ADD:
/*
** fold: ADD S,x
** ADD R,S
**
** into: ADD R,S
** ADD R,x
*/
if ((q->ptype&ADRMODE) == MINDEXED && q->preg == q->pindex) return;
q->preg = p->preg; /* fix up operation */
swappseudo(p,q); /* swap the two */
foldplus(q); /* optimize earlier code some more */
q = before(p); /* pick it up again */
if (q != NULL && q->pop == MOVE && q->ptype == REGIS &&
q->preg == p->preg) {
/*
** fold: MOVE R,S
** ADD R,x
**
** into: ADD S,x
** MOVE R,S
*/
p->preg = q->pr2; /* fix up register again */
swappseudo(p,q); /* switch them around again */
p = q; /* forget the move, hack the add */
}
break; /* end case ADD, SUB */
case AOS:
case SOS:
case MOVE:
case MOVN:
if (((q->ptype & ADRMODE) == MINDEXED && q->pindex == p->preg) ||
(q->ptype & IND) || (b = before(q)) == NULL ||
((r = rchange (b->pop)) != REG_CHANGED && r != REG_SET) ||
b->preg != p->preg || !(b->ptype & IMM) || prevskips (b) ||
((b->ptype & ADRMODE) == MINDEXED && q->preg == b->pindex)) break;
/*
** fold: ADDI R,x
** AOS S,y
** ADD R,S
**
** into: AOS S,y
** ADDI R,x
** ADD R,S
**
** so the immediate add can propagate to the list start...
*/
swappseudo (b, q); /* thats all folks! */
} /* end switch(q->pop) */
r = p->preg; /* remember register */
q = p; /* start up just before op */
if (!prevskips (p)) while ((q = before(q)) != NULL && !prevskips (q) &&
q->preg == r && p->pop == ADD) {
switch (q->pop) {
case MOVN: case SUB: case SETO: case SETZ:
if (!unsetz (q)) break; /* turn into MOVE or ADD */
case MOVE: case ADD:
switch (p->ptype) {
case IMMED:
switch (q->ptype) {
case IINDEXED:
case IMMED:
/*
** fold: MOVEI R,addr(i)
** ADDI R,c
**
** into: MOVEI R,addr+c(i)
*/
q->poffset += p->pvalue;
p->pop = NOP; /* poof! */
fixprev();
p = q;
continue;
case REGIS:
if (q->pop == MOVE) {
/*
** fold: MOVE R,S
** ADDI R,x
**
** into: ADDI S,x
** MOVE R,S
*/
if ((q->ptype&ADRMODE) == MINDEXED && q->preg == q->pindex)
return;
swappseudo(p, q); /* swap the ops */
r = q->preg = p->pr2; /* fix reg in ADDI */
p = q; /* move back over it */
}
default: if (q->pop == ADD) continue;
} /* end switch(q->ptype) */
break; /* propagate break to escape loop */
case IINDEXED:
switch (q->ptype) {
case IINDEXED:
if ((q->pptr != NULL && p->pptr != NULL) ||
(q->pindex != 0 && p->pindex != 0)) continue;
case IMMED:
/*
** fold: MOVEI R,c
** ADDI R,addr(i)
**
** into: MOVEI R,addr+c(i)
*/
p->pop = q->pop;
q->pop = NOP;
p->poffset += q->poffset;
if (q->ptype == IINDEXED) {
if (p->pptr == NULL) p->pptr = q->pptr;
if (p->pindex == 0) p->pindex = q->pindex;
}
continue;
case REGIS:
if (q->pop == MOVE) {
/*
** fold: MOVE R,S
** ADDI R,x
**
** into: ADDI S,x
** MOVE R,S
*/
swappseudo(p, q); /* swap the ops */
r = q->preg = p->pr2; /* fix reg in ADDI */
p = q; /* move back over it */
}
continue;
default:
if ((q->ptype&ADRMODE) == MINDEXED && q->preg == q->pindex)
return;
if (q->pop != MOVE || p->pindex == 0) continue;
p->pop = q->pop; /* turn MOVE+XADDI into */
q->pop = ADD; /* XMOVEI+ADD to save one */
swappseudo(p, q); /* instruction... */
p = q; /* look at correct one next loop */
}
continue; /* fall through goes back */
case REGIS:
if (q->ptype == IINDEXED && q->pindex == 0) {
/*
** fold: XMOVEI R,x
** ADD R,S
**
** into: XMOVEI R,x(S)
*/
q->pindex = foldcse (p->pr2, before (q)); /* new index */
p->pop = NOP; /* drop now useless move */
fixprev(); /* maybe that was last instr in buf */
return; /* not likely any more to do */
}
if ((q->ptype & ADRMODE) == MINDEXED && q->pindex == r) {
if (!(q->ptype & IMM) || q->pop != MOVE) break;
/*
** fold: XMOVEI R,x(R)
** ADD R,S
**
** into: ADD R,S
** XMOVEI R,x(R)
**
** (avoid silly checks and opcode switches below
*/
} else if (q->ptype == REGIS && q->pop == MOVE) {
/*
** fold: MOVE R,S
** ADD R,T
**
** into: ADD S,T
** MOVE R,S
*/
if (q->pr2 == SP) return; /* don't break stack */
p->preg = q->pr2; /* set register */
} else {
/*
** fold: MOVE R,x
** ADD R,S
**
** into: MOVE R,S
** ADD R,x
*/
if ((q->ptype&ADRMODE) == MINDEXED && q->preg == q->pindex)
return;
p->pop = q->pop; /* set opcode */
q->pop = ADD; /* to swap them */
}
swappseudo(p,q); /* opcodes hacked, swap ops */
foldplus (q); /* try again lower down */
if (p->ptype == REGIS) return; /* avoid infinite loop */
q = p; /* start after the swap */
continue; /* try loop again */
default:
switch (q->ptype) {
case IINDEXED:
if (q->pindex != 0) continue; /* bad swap */
case IMMED: /* swap order of immed and */
p->pop = q->pop; /* memory MOVE/ADD so that */
q->pop = ADD; /* ADDI can be optimized */
swappseudo(p, q); /* even further. */
break; /* not likely to find any more */
case REGIS:
if (q->pop == MOVE) {
/*
** fold: MOVE R,S
** ADDI R,x
**
** into: ADDI S,x
** MOVE R,S
*/
swappseudo(p, q); /* swap the ops */
r = q->preg = p->pr2; /* fix reg in ADDI */
p = q; /* move back over it */
} /* end REGIS: if (q->pop==MOVE) */
default: if (q->pop == ADD) continue;
} /* end default: switch(q->ptype) */
} /* end switch(p->ptype) */
} /* end switch(p->pop) */
break; /* default: dont keep looping */
} /* end while(q->preg == p->preg) */
switch (p->ptype &~ (IND + SKIPPED)) {
case IMMED:
/*
** fold: ADDI R,[-n]
** into: SUBI R,n
*/
if (p->pvalue < 0) {
switch (p->pop) {
case ADD:
p->pop = SUB;
break;
case MOVE:
p->pop = MOVN;
break;
case SUB:
p->pop = ADD;
break;
case MOVN:
p->pop = MOVE;
break;
default: return;
}
p->pvalue = - p->pvalue;
} else if (p->pvalue == 0) {
switch (p->pop) {
case ADD:
case SUB:
p->pop = NOP; /* fold: ADDI R,0 */
fixprev(); /* into: nothing */
return;
case MOVN:
case MOVE:
p->pop = SETZ; /* fold: MOVEI R,0 */
p->ptype = ONEREG; /* into: SETZ R, */
return;
}
}
if (p->pvalue == 1 && p->pop == MOVN) {
p->pop = SETO; /* fold: MOVNI R,1 */
p->ptype = ONEREG; /* into: SETO R, */
return;
}
break;
case IINDEXED:
if (p->pop == ADD && p->pindex == 0) {
/*
** fold: ADDI R,addr
** into: MOVEI R,addr(R)
*/
p->pop = MOVE; /* make XMOVEI */
p->pindex = p->preg; /* with index same as reg */
if ((q = before(p)) != NULL && q->ptype == REGIS && q->pop == MOVE
/* !prevskips (p or q) */ && q->preg == p->pindex) {
/*
** fold: MOVE R,S
** MOVEI R,addr(R)
**
** into: MOVEI R,addr(S)
*/
p->pindex = q->pr2; /* flatten index calculation */
q->pop = NOP; /* flush useless MOVE */
}
p->pindex = foldcse(p->pindex, before(p)); /* fold idx */
}
break;
case MINDEXED:
if (p->pop != ADD+BOTH || (q = before (p)) == NULL ||
prevskips (p) || prevskips (q) || q->preg != p->preg) break;
if (!unsetz (q) || q->ptype != IMMED) break; /* make sure MOVEI */
switch (q->pvalue) {
case -1:
q->pop = NOP; /* fold: MOVNI R,1 */
p->pop = SOS; /* ADDB R,x */
foldinc (p); /* into: SOS R,x */
break;
case 0:
q->pop = NOP; /* fold: MOVEI R,0 */
p->pop = MOVE; /* ADDB R,x */
foldmove(p); /* into: MOVE R,x */
break;
case 1:
q->pop = NOP; /* fold: MOVEI R,1 */
p->pop = AOS; /* ADDB R,x */
foldinc (p); /* into: AOS R,x */
break;
default:
foldplus (q); /* put back more efficient op */
}
}
}
/*
** Optimize ADJBP instruction.
**
** foldbyte(p)
** takes an ADJBP instruction in the peephole buffer,
** and tries to fold it out (e.g. by turning it into an ADDI).
*/
void foldbyte (p)
pcode p;
{
pcode q, n;
int a, b, o, r, s;
/* Make sure we're looking at an unskipped ADJBP */
if (p->pop != ADJBP || prevskips (p) || !optimize) {
foldmove (p); /* just run cse */
return;
}
/*
** Find the instruction that sets R.
**
** This is assumed to be the last one with the register
** field set to R, but we have to be careful of double
** operations and PUSHJs.
*/
n = p; /* start looking just before ADJBP */
while (1) { /* until we break out */
if (n->ptype == MINDEXED && n->pindex == p->preg) n = NULL;
else n = before (n); /* not index, safe to move back */
if (n != NULL) switch (rchange (n->pop)) {
case REG_SAME: case REG_SET: /* nice single word op? */
case REG_SET_DBL: case REG_CHANGED: case REG_CHG_DBL:
if (n->preg == p->preg) break; /* the one we want */
continue; /* else look back some more */
case DBL_SAME: case DBL_SET: /* nasty double word op? */
case DBL_SET_SGL: case DBL_CHANGED: case DBL_CHG_SGL:
if (n->preg != p->preg && n->preg - 1 != p->preg) continue;
default: /* PUSHJ? */
n = NULL; /* give up */
}
if (n == NULL || prevskips (n)) { /* backed off end or hit nasty op? */
foldmove (p); /* nothing to fold into, try cse */
return;
}
break; /* found matching sgl reg op, escape */
}
/*
** Now see what kind of op we have.
**
** If it is a SETZ, we simply turn the ADJBP into a MOVE.
** If it is a MOVNI or SETO or SUBI, we turn it into a MOVEI or ADDI.
** Otherwise, if it is not a MOVEI or ADDI we give up.
*/
switch (n->pop) { /* make sure we can hack it */
case SETZ:
n->pop = NOP;
case SETZ+BOTH:
n->preg = SCRREG; /* take out reg if not whole op */
p->pop = MOVE; /* change null ADJBP into MOVE */
foldmove(p); /* try further optimizations */
return; /* all done */
case MOVN: case SUB: case SETO: case MOVE: case ADD:
if (unsetz (n) && n->ptype == IMMED) break;
default:
foldmove(p);
return;
}
/* Fold in with byte constructor */
if (p->ptype == REGIS && (q = before(p)) != NULL && q->ptype == MINDEXED
&& q->pptr == &bytsym /* prevskips checked with ptype */) {
/*
** fold: MOVEI R,n
** IOR S,$BYTE+m
** ADJBP R,S
**
** into: ADDI S,i
** IOR S,$BYTE+j
** MOVE R,S
*/
if (q->poffset > 3) { /* 7-bit ASCII? */
o = n->pvalue + q->poffset - 4;
if (o < 0) {
b = 8 - ((4 - o) % 5);
o = - ((4 - o) / 5);
} else {
b = o % 5 + 4;
o /= 5;
}
} else {
o = n->pvalue + q->poffset;
if (o < 0) {
b = 3 - ((3 - o) % 4);
o = - ((3 - o) / 4);
} else {
b = o % 4;
o /= 4;
}
}
a = (n->pop == MOVE) ? MOVE : ADJBP; /* see which inst to end with */
r = p->preg; /* get registers of ADJBP for later */
s = p->pr2;
n->pop = NOP; /* flush old instructions */
q->pop = NOP;
p->pop = NOP;
fixprev();
code1 (ADD, s, o); /* make new instructions */
code7 (IOR, s, &bytsym, b, 0);
code0 (a, r, s);
return;
}
/* That lost. Maybe only changing by one. */
if (n->pop == MOVE && n->pvalue == 1) {
n->pop = NOP; /* change MOVEI R,1 + ADJBP R,x */
p->pop = MOVE; /* into MOVE R,x + IBP R */
r = p->preg;
foldmove(p); /* optimize the MOVE */
/* make IBP without optimization */
newcode();
previous->ptype = REGIS;
previous->pop = IBP;
previous->pr1 = 0;
previous->pr2 = r;
return;
}
foldplus(n); /* all optimization failed, fix up */
}
/*
** Make operations of type BOTH.
**
** foldboth()
** looks at the last instruction in the peephole buffer,
** and if it is a MOVEM tries to fold it into an OPB.
*/
void foldboth()
{
pcode p, b, q;
int badidx, r, s;
foldmove(previous); /* maybe it's a MOVE or LDB */
if (prevskips (previous)) return; /* skipped over, can't hack */
if (previous->pop == ADD+BOTH) foldplus (previous); /* try += into ++ */
/*
** fold: ADJSP 17,n
** ...
** MOVEM R,1-n(17)
**
** into: ...
** PUSH 17,R
** ADJSP 17,n-1
*/
if (previous->pop == MOVEM && previous->ptype == MINDEXED &&
previous->pindex == SP) /* if MOVEM onto stack */
for (p = before (previous); p != NULL; p = before (p)) /* looking back */
switch (p->pop & OPCODE) { /* through opcodes... */
case JRST: case JUMP: case AOJ: case SOJ: /* control flow mungage */
case PUSH: case POP: case POPJ: /* or stack mungage */
case SPUSH: case SPOP: /* or stack struct mungage */
p = NULL; /* give up */
break;
case ADJSP: /* the one we want */
if (prevskips (p) || previous->poffset != 1 - p->pvalue) {
p = NULL; /* wrong number or skipped */
break; /* lose lose */
}
while ((q = after (p)) != previous) { /* until right before it */
if ((q->pindex & REGMASK) == SP) switch (q->ptype & ADRMODE) {
case MINDEXED: case BYTEPOINT: /* indexed types */
q->poffset += p->pvalue; /* adjust for hacked stack */
}
swappseudo (p, q); /* bubble ADJSP forward */
p = q; /* point to where it is now */
}
swappseudo (p, previous); /* now swap MOVEM and ADJSP */
p->pop = PUSH; /* make PUSH in place of MOVEM */
p->ptype = REGIS; /* this is now a register op */
p->pr2 = p->preg; /* with second reg old first */
p->preg = SP; /* and first reg stack */
if ((-- previous->pvalue) == 0) { /* diminish ADJSP by one */
previous->pop = NOP; /* it's gone completely away */
fixprev(); /* so go back to before it */
}
if ((q = before (p)) != NULL && q->preg == p->pr2 && !prevskips (q))
switch (q->pop) {
case SETZ: case SETO: case MOVN:
if (!unsetz (q)) break; /* turn SETZ into MOVE */
case MOVE:
/*
** fold: MOVE R,x
** PUSH 17,R
**
** into: PUSH 17,x
** MOVE R,0(17)
**
** in hope that genrelease() will flush the MOVE.
*/
if (q->ptype == IINDEXED) break; /* no XPUSHI */
p->pop = MOVE; /* get different MOVE instr */
p->ptype = MINDEXED; /* from memory, no indirection */
p->preg = q->preg; /* into destination register */
p->pindex = SP; /* from stack */
p->pptr = 0; /* not global variable space */
p->poffset = 0; /* top of stack */
q->pop = PUSH; /* now make old MOVE into PUSH */
q->preg = SP; /* onto stack */
}
return; /* no more to do here */
default:
if ((p->pr2 & REGMASK) == SP) switch (p->ptype & ADRMODE) {
case MINDEXED:
case BYTEPOINT:
if (p->poffset < previous->poffset) continue; /* arg, safe */
case REGIS:
p = NULL; /* OP R,17 loses big */
break;
}
}
if ((b = before(previous)) != NULL && b->ptype == REGIS /* !prevskips */ &&
b->pop == MOVE && b->preg == previous->preg && p->pop != IDIV) {
/*
** fold: MOVE R,S
** OP R,x
**
** into: OP S,x
** MOVE R,S
*/
previous->preg = b->pr2; /* flatten the register change */
swappseudo(b, previous); /* switch the two ops */
p = b; /* forget about the MOVE */
} else b = p = previous; /* no move, start at the top */
if (p->ptype != MINDEXED) return;
switch (p->pop) {
case AOS:
case SOS:
if ((b = before (p)) != NULL && b->pop == MOVEM &&
b->ptype == MINDEXED /* !prevskips */ && b->pptr == p->pptr &&
b->pindex == p->pindex && b->poffset == p->poffset) {
/*
** fold: MOVEM R,x
** AOS S,x
**
** into: ADDI R,1
** MOVEM R,x
** MOVE S,R
*/
b->pop = ADD; /* make ADD */
b->ptype = IMMED; /* immediate quantity */
b->pvalue = (p->pop == AOS)? 1 : -1; /* by one */
s = p->preg; /* remember the reg we want */
p->pop = MOVEM; /* now make MOVEM */
r = p->preg = b->preg; /* using old register */
foldplus(b); /* fix up addition */
code0 (MOVE, s, r); /* and fix up registers */
} else foldinc (p);
default: return;
case MOVEM: break;
}
/*
** At this point we know that p = previous points to a MOVEM.
** Find a likely candidate op for folding.
*/
badidx = 0;
while (1) {
b = before(b); /* skip over unlikely candidate */
if (b == NULL) return; /* ran out of them */
if ((b->ptype & ADRMODE) == MINDEXED && b->pindex == p->preg) return;
if (b->preg == p->pindex) badidx = 1; /* can't fold unless SETZ */
switch (rchange (b->pop)) {
case REG_SAME: case REG_SET: /* nice single word op? */
case REG_SET_DBL: case REG_CHANGED: case REG_CHG_DBL:
if (b->preg == p->preg) break; /* the one we want */
continue; /* else look back some more */
case DBL_SAME: case DBL_SET: /* nasty double word op? */
case DBL_SET_SGL: case DBL_CHANGED: case DBL_CHG_SGL:
if (!prevskips (b) && (b->pop == DFMP || b->pop == DFDV) &&
b->preg == p->preg && (q = before (b)) != NULL &&
!prevskips (q) && q->pop == SETZ && q->preg == p->preg + 1) {
/*
** fold: SETZ R+1
** DFMP R,x
** MOVEM R,y
**
** into: FMPR R,x
** MOVEM R,y
**
** so that later optimization can turn it into a FMPRB.
** The low order word of the double can't affect the result,
** but only for multiply -- this is not safe for DFAD and DFSB!
*/
if (q->ptype == ONEREG) q->pop = NOP; /* drop SETZ */
b->pop = ((b->pop == DFMP)? FMPR : FDVR); /* make singleword */
break; /* escape loop */
}
if (b->preg - 1 == p->pindex) badidx = 1;
if (b->preg != p->preg && b->preg - 1 != p->preg) continue;
default: /* PUSHJ? */
return; /* give up */
}
break;
}
if (prevskips (b)) return; /* skipped over, lose */
q = before (b);
while (q != NULL && q->preg != b->preg && q->preg != p->preg &&
q->preg != p->pindex) {
switch (q->pop) {
case FADR: case FSBR: case FDVR: case FMPR:
case ADD: case IMUL: case SUB: case MOVE:
case MOVN: case SETZ: case SETO: case FLTR:
case FIXR: case AND: case IOR: case SETCM:
q = before (q);
continue;
}
break; /* break out with unknown op */
}
switch (b->pop) {
case FMPR:
case IMUL:
if (q != NULL && q->pop == MOVN && q->preg == p->preg && !badidx &&
q->ptype == p->ptype /* !prevskips */ && q->pptr == p->pptr &&
q->poffset == p->poffset && q->pindex == p->pindex) {
/*
** fold: MOVN R,x
** IMUL R,y
** MOVEM R,x
**
** into: MOVN R,y
** IMULB R,x
*/
p->pop = b->pop | BOTH; /* move mply op over */
b->pop = MOVN; /* make old add into negated move */
q->pop = NOP; /* flush the first move */
foldplus(p); /* maybe we can turn it into an AOS */
break;
}
case ADD:
case AND:
case IOR:
case XOR:
case SUB:
case FADR:
case FSBR:
if (q != NULL && q->pop == MOVE && q->preg == p->preg && !badidx &&
q->ptype == p->ptype /* !prevskips */ && q->pptr == p->pptr &&
q->poffset == p->poffset && q->pindex == p->pindex) {
/*
** fold: MOVE R,x
** ADD R,y
** MOVEM R,x
**
** into: MOVE R,y
** ADDB R,x
*/
if (b->pop == SUB) {
p->pop = ADD+BOTH; /* make ADD */
b->pop = MOVN; /* and MOVN */
} else if (b->pop == FSBR) {
p->pop = FADR+BOTH; /* make floating add */
b->pop = MOVN; /* and floating MOVN (same as int) */
} else {
p->pop = b->pop | BOTH; /* move add op over */
b->pop = MOVE; /* make old add into move */
}
q->pop = NOP; /* flush the first move */
if (b->ptype == REGIS
&& (b->pop == MOVE || pushneg(b->pr2, before (b)))) {
b->pop = NOP;
p->preg = b->pr2;
code0 (MOVE, b->preg, b->pr2);
}
foldplus(p); /* maybe we can turn it into an AOS */
break;
}
case MOVE: case MOVN: case MOVM: case SETCM:
case FDVR: case IDIV: /* yes, IDIVB works */
if (b->ptype == p->ptype && b->poffset == p->poffset &&
b->pindex == p->pindex && b->pptr == p->pptr && !badidx) {
/*
** fold: OP R,addr
** MOVEM R,addr
**
** into: OPB R,addr
*/
p->pop = b->pop; /* move opcode across */
if (b->pop != MOVE) p->pop |= BOTH;
b->pop = NOP; /* flush old instruction */
foldplus(p); /* maybe we can turn it into an AOS */
}
break;
case SETZ:
case SETO:
if (b->ptype == ONEREG) {
/*
** fold: SETZ R,
** MOVEM R,x
**
** into: SETZB R,x
*/
p->pop = b->pop+BOTH; /* move opcode across, make both */
b->pop = NOP; /* flush old instruction */
}
}
}
/*
** Optimize ADJSP into previous instructions
**
** foldstack (n)
** attempts to pull previous ADJSPs, PUSHs, etc into a new ADJSP
** which would, if no such optimization occurred, have constant n.
** The return value is what the constant should be taking into
** account the optimization which has been done.
*/
int foldstack(n)
{
pcode p = previous;
int maxfold;
maxfold = (stackrefs? 0 : 1000000); /* limit on pos ADJSP carryacross */
while (p != NULL) switch (p->pop & OPCODE) {
case ADJSP:
if (prevskips (p) || p->pvalue > maxfold) return n;
n += adjstack (p->pvalue, p);
n += p->pvalue;
maxfold -= p->pvalue;
p->pop = NOP;
fixprev();
p = previous;
break;
case PUSH:
if (((p->ptype == IMMED && p->pvalue >= -1 && p->pvalue <= 0)
|| p->ptype == REGIS) && !prevskips (p)) {
p = before(p); /* PUSH 17,[0] or ..[-1] can become */
maxfold = 0; /* Don't fold pos ADJSP before here */
break;
}
return n;
case POP:
if (prevskips (p) || p->ptype != REGIS) return n;
maxfold++; /* account for stack space used */
p = before(p);
break;
case MOVE:
if (p->ptype == MINDEXED && p->pindex == SP && p->poffset == 0 &&
n < 0 && !prevskips (p)) {
/*
** fold: MOVE R,0(17)
** into: POP 17,R
*/
n += makepop (p); /* turn into POP */
p = previous;
maxfold = (stackrefs? 0 : 1000000); /* start again */
break;
}
if ((p->ptype & ADRMODE) == MINDEXED && p->pindex == SP &&
p->poffset > - maxfold) maxfold = - p->poffset;
p = before(p);
break;
case MOVEM:
if (p->ptype == MINDEXED && p->pindex == SP &&
p->poffset == 1 && n > 0 && !prevskips (p)) {
/*
** fold: MOVEM R,1(17)
** into: PUSH 17,R
*/
p->ptype = REGIS;
p->pr2 = p->preg;
p->preg = SP;
p->pop = PUSH;
n += adjstack(-1, p);
n--;
p = previous;
maxfold = (stackrefs? 0 : 1000000); /* start again */
break;
}
case ADD: case SUB: case IMUL: case IDIV:
case FADR: case FSBR: case FDVR: case FMPR:
case AND: case IOR: case XOR: case CAI:
case MOVN: case SETCM: case SETZ: case CAM:
case SETO: case AOS: case SOS: case LSH:
case ADJBP: case SUBBP: case BPCNT: case IBP:
case ILDB: case IDPB: case LDB: case DPB:
case HLRZ: case HRLM: case HRRZ: case HRRM:
case TDC: case TDN: case TDO: case TDZ:
case TLC: case TLN: case TLO: case TLZ:
case TRC: case TRO: case TRN: case TRZ:
case SKIP:
if ((p->ptype & ADRMODE) == MINDEXED && p->pindex == SP &&
p->poffset > - maxfold) maxfold = - p->poffset;
p = before(p);
break;
default:
return n;
}
return n;
}
/*
** Turn MOVE instruction into POP
**
** This turns the given MOVE R,0(17) into POP 17,R and fixes up
** the surrounding instructions. It returns the result of
** adjstack() + 1 (for the new extra pop).
*/
makepop (p)
pcode p;
{
pcode q, b;
/* first make the pop */
p->ptype = REGIS;
p->pr2 = p->preg;
p->preg = SP;
p->pop = POP;
/* now see if we can further fold it */
if (p->pr2 != RETVAL && (q = after (p)) != NULL && q->pop == MOVEM &&
q->preg == p->pr2) {
for (b = q; b != NULL && q != NULL; b = after (b)) {
if ((b->pr2 & REGMASK) == p->pr2) switch (b->ptype & ADRMODE) {
case MINDEXED: case BYTEPOINT: case REGIS:
q = NULL; /* used as index, signal lossage */
}
if (b != q) switch (rchange (b->pop)) {
case REG_CHG_DBL:
if (b->ptype == REGIS && b->pr2 == p->pr2 - 1) q = NULL;
case REG_SAME: case REG_CHANGED:
if (b->preg == p->pr2) q = NULL; /* our register, lose */
break;
case REG_SET_DBL:
if (b->ptype == REGIS && b->pr2 == p->pr2 - 1) q = NULL;
case REG_SET:
if (b->preg == p->pr2) b = NULL; /* our reg safely set */
break;
case DBL_SAME: case DBL_CHANGED:
if (b->ptype == REGIS && b->pr2 == p->pr2 - 1) q = NULL;
case DBL_CHG_SGL:
if (b->preg == p->pr2 || b->preg == p->pr2 - 1) q = NULL;
break;
case DBL_SET:
if (b->ptype == REGIS && b->pr2 == p->pr2 - 1) q = NULL;
case DBL_SET_SGL:
if (b->preg == p->pr2 || b->preg == p->pr2 - 1) b = NULL;
break;
default:
q = NULL; /* reg prob not used but be safe */
}
}
if (q != NULL) { /* found one? */
q->pop = POP; /* yes, make POP */
q->preg = p->preg; /* on stack */
if ((q->ptype & ADRMODE) == MINDEXED && q->preg == q->pindex)
q->poffset--; /* adjust if from stack */
p->pop = NOP; /* drop register POP */
p = q; /* use this one instead */
}
}
return adjstack (1, p) + 1;
}
/*
** Fix up old ops after stack change shifts.
**
** This is an auxiliary routine used by foldstack().
** The section of the buffer changed is assumed to be safe.
** It returns the net change to the stack offset.
*/
static int
adjstack(n, p)
pcode p;
{
pcode q;
int c; /* how much we changed it */
c = 0;
q = previous;
while (q != p) {
switch (q->ptype & ADRMODE) {
case MINDEXED: /* if the indexing is on the stack */
if (q->pindex == SP) q->poffset += n; /* then need to adjust */
break;
case BYTEPOINT:
if ((q->pindex & REGMASK) == SP) q->poffset += n;
break;
case REGIS: /* push/pop turns into move/movem */
if (q->preg == SP) switch (q->pop) {
case POP:
c += adjstack(-1, q);
q->pop = MOVE;
q->preg = q->pr2;
q->ptype = MINDEXED;
q->pptr = NULL;
q->pindex = SP;
q->poffset = n;
c--;
break;
case PUSH:
c += adjstack(1, q);
q->pop = MOVEM;
q->preg = q->pr2;
q->ptype = MINDEXED;
q->pptr = NULL;
q->pindex = SP;
q->poffset = n + 1;
c++;
break;
} /* end case REGIS: switch(q->pop) */
case RCONST: /* push becomes setzb/setob */
if (q->pop == PUSH) {
c += adjstack(1, q);
q->pop = (q->pvalue == 0)? SETZ+BOTH : SETO+BOTH;
q->preg = SCRREG; /* have to have some reg here */
q->ptype = MINDEXED;
q->pptr = NULL;
q->pindex = SP;
q->poffset = n + 1;
c++;
break;
}
} /* end switch(q->ptype) */
q = before(q); /* back until hit the changed op */
}
return c;
}
/*
** Pull beginning-of-routine ADJSP across label
**
** Often some code will start out with if (cond) return;
** if there are local variables in the routine they will be made
** and unmade unnecessarily while the condition is checked.
** Here we attempt to fix that up by pulling the ADJSP for the
** local variables across the if and return.
**
** Our argument is the label we are about to emit;
** the return value is the ADJSP that must be done
** after the label is (or is not) emitted.
*/
int hackstack (lab)
struct SYMBOL *lab;
{
int spos, ceiling;
pcode lastadj, p;
if (!optimize) return 0;
if (isskip(previous->pop)) return 0;
spos = stackoffset; /* remember where we are in stack */
ceiling = 1000000; /* assume most of stack is free */
lastadj = NULL; /* haven't found an ADJSP yet */
for (p = previous; p != NULL; p = before (p)) switch (p->pop & OPCODE) {
case JRST: case JUMP: case AOJ: case SOJ:
if (p->pptr != NULL && p->pptr->sclass == SILABEL) {
if (p->pptr != lab) return 0;
continue;
}
case POPJ:
spos = 0; /* stack can't exist here */
lastadj = NULL; /* so don't remember ADJSP after */
continue;
case PUSH:
spos--;
case MOVE: case MOVN: case ADD: case SUB: case IMUL: case IDIV:
case FADR: case FSBR: case FMPR: case FDVR: case AND: case IOR:
case DFAD: case DFSB: case DFMP: case DFDV: case XOR: case SETZ:
case SETO: case SKIP: case CAI: case CAM: case TRO: case TRN:
case TRC: case TDO: case TDZ: case TLZ: case TLO: case TDC:
case TDN: case LDB: case ADJBP: case PUSHJ: case MOVEM: case MOVM:
case AOS: case SOS:
if ((p->ptype & ADRMODE) != MINDEXED || p->pindex != SP) continue;
if (p->poffset + spos < 0) continue;
if (p->poffset + spos >= ceiling) continue;
ceiling = p->poffset + spos;
if (ceiling <= 1) return 0;
continue;
case ADJSP:
if (p->pvalue < 0) {
if (lastadj == NULL && spos == 0) lastadj = p;
spos -= p->pvalue; /* change stack offset */
continue;
}
/*
** If we got here, we've finally found our positive ADJSP.
**
** First we check it against the ceiling we've calculated,
** to make sure all above-the-return-value stack references
** were to function arguments rather than to variables.
**
** Then we run through the ops between here and the final ADJSP
** (the one before the POPJ) or the end of the buffer, adjusting
** all stack references to account for the variables being
** dropped out of the middle like the tablecloth trick.
*/
if (p->pvalue >= ceiling) return 0; /* stack used, can't pull across */
spos = ceiling = p->pvalue; /* else remember how much to move */
p->pop = NOP; /* drop the ADJSP */
while ((p = after (p)) != lastadj) {
if ((p->ptype & ADRMODE) == MINDEXED && p->pindex == SP &&
p->poffset + spos < 0) p->poffset += ceiling;
switch (p->pop) {
case PUSH:
spos++;
break;
case ADJSP:
spos += p->pvalue;
break;
}
}
if (p != NULL) { /* still have to deal with the ADJSP */
p->pvalue += ceiling; /* take out the variable part */
if (p->pvalue == 0) p->pop = NOP; /* or drop the whole thing */
}
return ceiling; /* this is how much we adjusted */
default: return 0; /* some strange opcode */
}
return 0; /* broke out means no ADJSP in block */
}
/*
** Get rid of unused locals on return.
**
** Often code before a return will contain stores to local variables
** that are no longer used because of common subexpression elimination.
** Here we eliminate those stores as well.
*/
#define MAXSTACK 100 /* largest expected stack var */
killstack()
{
pcode p;
int stkused[MAXSTACK + 1], i, op, r, s;
if (stackrefs != 0) return; /* funny stack usage, give up */
for (i = 0; i <= MAXSTACK; i++) stkused[i] = 0; /* no stack usage yet */
p = previous; /* start at the top */
while (p != NULL) switch (p->pop & OPCODE) {
case MOVEM: case AOS: case SOS: case DMOVEM:
case SETCM: case MOVN: case SETO: case SETZ:
case FADR: case FSBR: case FMPR: case FDVR:
case ADD: case IMUL: case SUB: case AND:
case IOR: case XOR: case IBP: case ADJBP:
case DPB: case LDB: case IDPB: case ILDB:
case MOVE: case LSH: case FIXR: case FLTR:
case HRRZ: case HRRM: case HLRZ: case HRLM:
case DFAD: case DFSB: case DFMP: case DFDV:
case TDO: case TDZ: case TDN: case TDC:
case TRO: case TRZ: case TRN: case TRC:
case TLO: case TLZ: case TLN: case TLC:
case CAI: case CAM: case SKIP:
/*
** Found an op that we know is safe for stack var elimination.
** First, see if it is really a stack variable reference.
*/
switch (p->ptype & ADRMODE) {
case BYTEPOINT:
if ((p->pindex & REGMASK) == SP) break;
case MINDEXED:
if (p->pindex == SP) break;
default:
p = before(p); /* not stack ref, ignore */
continue;
}
/*
** It is a stack reference. See where on the stack it is.
** If out of our range, throw it in one catchall heap.
** Now check if we've seen that offset already.
*/
i = - p->poffset; /* get offset from stack */
if (i < 0 || i > MAXSTACK) i = MAXSTACK; /* fold range */
if (stkused[i]) { /* already used that offset? */
p = before(p); /* yes, back up */
continue; /* and try for more */
}
/*
** Haven't seen that offset, so if it is an assignment
** we can safely throw it away.
*/
if (!(p->ptype & IND)) switch (p->pop) {
case MOVEM:
case HRRM:
case HRLM:
case DMOVEM:
p->pop = NOP; /* drop losing MOVEM */
fixprev(); /* make sure fully gone */
p = before(p); /* skip back to avoid remembering */
continue; /* this as a stack ref */
case SETO:
case SETZ:
p->ptype = ONEREG; /* ignore all memory in op */
default:
if ((op = (p->pop &~ BOTH)) == p->pop) break; /* nothing to do */
p->pop = MOVE; /* make MOVE for cse check */
s = foldcse (r = p->preg, p); /* look for it in another reg */
if (p->pop != NOP) {
p->pop = op; /* not found, put op back */
break; /* leave switch to mark offset used */
}
if (r != s) { /* found in a different reg */
p->ptype = REGIS; /* make register op */
p->pop = op; /* restore opcode */
p->preg = r; /* register */
p->pr2 = s; /* and other new register */
inskip (p); /* fold into previous SKIPA+MOVE */
}
p = before (p); /* get before to avoid stkused set */
continue; /* on with the show */
}
/*
** If we got this far, we are keeping at least some of the op.
** So remember that we need the memory kept for previous ops.
*/
stkused[i] = 1; /* this stack var is now active */
p = before(p); /* move back over op */
continue; /* and try for more stack kills */
case PUSH:
/*
** We found a PUSH, and since we can't handle ADJSP this must
** have been a MOVEM to the then top of the stack.
**
** What we do is turn it into an ADJSP 17,1, and then return.
** What we want to do is go back doing the same for previous
** pushes, and then do a foldstack() so that they all get
** collapsed into one ADJSP. But that would take more work
** than I care to do right now.
*/
if (!stkused[0]) { /* has top of stack been used? */
p->pop = ADJSP; /* no, turn into ADJSP */
p->ptype = RCONST; /* of constant amount */
p->pvalue = 1; /* one word no longer pushed */
} /* then fall into unknown */
default:
return; /* unknown op, give up on it */
}
}
/*
** See if an instruction is safe to skip over.
**
** Takes an instruction as argument and returns true if that
** instruction will expand to one machine code word.
*/
int oneinstr(p)
pcode p;
{
switch (p->pop & OPCODE) {
case SUBBP: case BPCNT: case DFIX: case SPUSH: case SPOP:
case ADJBP: case DMOVE: case DMOVN: case DMOVEM:
case DFAD: case DFSB: case DFMP: case DFDV:
return 0; /* macro or simulated KL op */
case MOVE:
return 1; /* ok even for IINDEXED */
case TRN: case TRO: case TRC: case TRZ:
case TLN: case TLO: case TLC: case TLZ:
case LSH: case ASH: case CAI:
return (p->ptype != MINDEXED); /* IINDEXED for implicit immed op */
default: /* OPI R,addr expands to */
return (p->ptype != IINDEXED); /* XMOVEI 16,addr / OP r,16 */
}
}
/*
** Turn SETZ R, back into MOVEI R,0.
**
** Used by optimizations that want to only deal with MOVEs.
** Also inverts MOVNI to MOVEI and SUBI to ADDI.
**
** Returns 1 if the op is now a MOVE or ADD of some sort.
*/
int unsetz(p)
pcode p;
{
switch (p->pop) {
case MOVN:
case SUB:
if ((p->ptype &~ SKIPPED) != IMMED) return 0;
p->pvalue = - p->pvalue;
p->pop = (p->pop == MOVN? MOVE : ADD);
case MOVE:
case ADD:
return 1;
case SETZ:
case SETO:
if ((p->ptype &~ SKIPPED) != ONEREG) return 0;
p->ptype ^= (ONEREG ^ IMMED);
p->pvalue = (p->pop == SETZ? 0 : -1);
p->pop = MOVE;
return 1;
default:
return 0;
}
}