Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
kcc-5/kcc/ccopt.c
There are 8 other files named ccopt.c in the archive. Click here to see a list.
/* CCOPT.C - Peephole Optimizer - miscellaneous optimizations
**
** All changes after version 218 (8/8/85), unless otherwise specified, are
** Copyright 1985, 1986 by Ken Harrenstien, SRI International.
*/
/*
** 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 P_MOVE R,S back through peephole buffer
** cccse - finding registers already containing calculated value
** ccjskp - rearrangement of jump and skip instructions
*/
#include "cc.h"
#include "ccgen.h"
/* Imported functions */
extern int adjboffset(); /* from CCOUT */
extern PCODE *before(), *after(), *newcode(); /* CCCODE */
extern void fixprev(); /* CCCODE */
/* Exported functions */
PCODE *findrset();
int localbyte(); /* CCCODE (1 usage) */
void findconst(); /* CCCODE (1 usage) */
void foldplus(), foldbyte(), foldboth();
int foldstack(); /* CCCODE (1 usage) */
int hackstack(), oneinstr(), unsetz();
void killstack();
void optlsh(); /* CCCODE (1) */
/* Internal functions */
static int makepop(), adjstack();
static void foldinc();
/* LOCALBYTE(p, np) - Optimize usage of local byte pointer.
** P points to either an IOR S, or ADJBP S,.
** NP points to a succeeding instr, either a LDB R,S or DPB R,S.
**
** There may be several instructions in between. We know however
** that S is not used or referenced in between.
**
** The byte pointer will not be saved, so it is
** safe to make it a literal local-format BP. The return value
** is 1 if the op could be folded into the previous instructions,
** and 0 otherwise.
** This is only called from one place, in CCCODE.
*/
int
localbyte(p, np)
PCODE *p, *np;
{
int op = np->Pop;
int r = np->Preg;
int s = np->Pr2;
int b, i;
PCODE *q;
if (p
&& p->Pop == P_IOR
&& p->Ptype == PTA_PCONST /* && !prevskips(p) */
&& p->Preg == s
&& p->Pptr == NULL) {
/*
** fold: p-> IOR S,[<pconst>]
** ...
** np-> DPB R,S
**
** into: np-> DPB R,[ppss00,,(S)]
**
** and perhaps fold further with previous ops...
*/
b = TGSIZ_WORD - p->Pbsize * (p->Poffset+1); /* P field */
b = ((b&077)<<6) | (p->Pbsize & 077); /* add S field */
b <<= 6; /* shift into place */
flspop(p); /* drop the IOR from peephole buffer */
flspop(np); /* Drop the LDB/DPB too */
code2(op, r, b, s, (SYMBOL *)NULL, 0); /* make new op */
return 1; /* folded, say so */
} else if (p->Pop == P_ADJBP /* Check for ADJBP S,I */
&& p->Ptype == PTA_REGIS
&& p->Preg != p->Pr2 /* Avoid ADJBP S,S */
&& (q = findrset(before(p), p->Pr2)) != NULL
&& q->Pop == P_IOR /* Check for IOR I,[<pconst>] */
&& q->Ptype == PTA_PCONST
&& q->Pptr == NULL) {
PCODE *rp;
i = p->Pr2; /* Pick up I holding wordptr index */
/* Must verify that I is not used anywhere between the LDB and
** the IOR, except for the ADJBP itself.
*/
for (rp = before(np); rp && rp != q; rp = before(rp))
if (rinreg(rp, i) || rinaddr(rp, i)) /* Halt if ref seen */
if (rp != p) break; /* unless it's the ADJBP */
if (rp != q)
return 0; /* Ref seen or something, forget it. */
/*
** fold:
** q-> IOR I,[<pconst>]
** ...
** p-> ADJBP S,I
** ...
** np-> LDB/DPB R,S
**
** into: P_ADJBP S,[ppss00,,(I)]
** P_DPB R,S
*/
b = TGSIZ_WORD - q->Pbsize * (q->Poffset+1); /* P field */
b = ((b&077)<<6) | (q->Pbsize & 077); /* add S field */
b <<= 6; /* shift into place */
flspop(q); /* drop IOR */
flspop(p); /* and drop ADJBP */
flspop(np); /* and the LDB/DPB */
code2(P_ADJBP, s, b, i, (SYMBOL *)NULL, 0); /* Make new ADJBP */
code0(op, r, s); /* and new LDB/DPB */
return 1;
}
return 0;
}
/* OPTLSH(p) - Attempt to optimize LSH usage.
** We often get combinations of LSH and AND when dealing with
** PDP10 compability code. Someday this could be spiffed up to
** do LDBs or DPBs.
*/
void
optlsh(p)
PCODE *p;
{
PCODE *q;
unsigned long mask;
if (p->Pop == P_LSH /* Must be a LSH */
/* && !prevskips(p) */ /* that isn't skipped */
&& p->Ptype == PTA_RCONST /* and has constant operand */
&& p->Pvalue < 0 /* and is doing right shift */
&& (q = findrset(before(p), p->Preg))) { /* Find instr that sets R */
if (q->Pop == P_AND /* If it's AND (we know not skipped) */
&& q->Ptype == PTV_IMMED /* with immediate const operand */
&& (mask = ((unsigned)q->Pvalue >> (-p->Pvalue))) <= 0777777) {
/* Fold: into:
** AND R,[const] LSH R,-n
** ... ...
** LSH R,-n AND R,[const>>n]
** If const>>n will be small enough to fit in the RH, so we can
** generate ANDI instead of AND.
*/
swappseudo(p,q); /* Swap AND with LSH */
p->Pvalue = mask; /* Make AND's mask be shifted one */
}
}
}
/* FOLDINC(p) - Push P_AOS up into the loop it comes from.
**
** Often a for() loop will have a AOS/SOS (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 a previous P_MOVE of the same variable
** (sort of like a reverse common subexpression elimination).
**
** Fold: MOVE R,<E> to: AOS/SOS R,<E>
** ... ...
** AOS/SOS S,<E> --
**
** Intervening instructions must be checked to make sure that
** they do not modify the variable at <E> or the register R,
** and if R is used as an index reg, the offsets need to be fixed up.
** Stack level changes must be tracked so we can be sure whether an
** address reference is to <E> or not.
*/
static void
foldinc(p)
PCODE *p;
{
int safeindex[NREGS], i;
int usedreg;
int stkoff;
PCODE *q, *b;
if (prevskips(p) || !optobj) return;
/* <E> must be something plausible. Initialize the used-reg mask;
** bits which are 0 are safe for use as R.
*/
switch (p->Ptype & PTF_ADRMODE) {
#if 0 /* This isn't supported (probably never called with this anyway) */
case PTA_REGIS: usedreg = rbits[p->Pr2]; break;
#endif
case PTA_MINDEXED: usedreg = rbits[p->Pindex]; break;
default: return;
}
switch (p->Pop) {
case P_AOS: i = -1; break;
case P_SOS: i = 1; break;
default: return;
}
stkoff = 0;
q = p;
while ((q = before(q)) != NULL) switch (q->Pop) {
case P_PUSH:
if (q->Preg != R_SP) return;
++stkoff;
break;
case P_ADJSP: /* Stack adjustment */
if (p->Preg == R_SP)
stkoff += q->Pvalue;
break;
case P_MOVE:
if (sameaddr(p, q, stkoff)) { /* found what we want? */
if (prevskips(q) || (usedreg&(rbits[q->Preg])))
return;
q->Pop = p->Pop;
b = q;
do {
b = after(b);
if ((b->Ptype&PTF_ADRMODE) == PTA_MINDEXED
&& b->Pindex == q->Preg)
b->Poffset += i;
else if (b->Ptype == PTV_IMMED
&& b->Preg == q->Preg
&& b->Pop == P_IMUL)
i *= b->Pvalue;
} while (b != p);
i = p->Preg;
p->Pop = P_NOP; /* drop extra P_AOS */
fixprev();
code0(P_MOVE, i, q->Preg);
return;
}
if (q->Preg == p->Pindex) return;
usedreg |= rbits[q->Preg];
continue;
/* Ops that change a register (or two) */
case P_DFAD: case P_DFSB: case P_DFMP: case P_DFDV:
case P_DMOVE: case P_DMOVN: case P_IDIV: case P_UIDIV:
case P_DFIX: case P_SUBBP: case P_MUL:
case P_FADR: case P_FSBR: case P_FMPR: case P_FDVR:
case P_ADD: case P_IMUL: case P_SUB: case P_AND:
case P_IOR: case P_XOR: case P_ADJBP: case P_LSH:
case P_SETCM: case P_MOVN: case P_SETO: case P_SETZ:
case P_HRRZ: case P_HLRZ: case P_FIX: case P_FLTR:
case P_LDB:
if ((q->Ptype & PTF_ADRMODE) != PTA_MINDEXED) {
usedreg |= rbuse(q);
continue;
}
if (q->Preg == p->Pindex || sameaddr(p, q, stkoff))
return;
usedreg |= rbinreg(q);
continue;
/* Ops that change memory */
case P_POP:
if (q->Preg != R_SP) return;
--stkoff;
/* Fall thru since op changes mem */
case P_MOVEM:
case P_DPB: case P_ILDB: case P_IDPB: case P_IBP:
case P_FADR+POF_BOTH: case P_FSBR+POF_BOTH:
case P_FMPR+POF_BOTH: case P_FDVR+POF_BOTH:
case P_ADD+POF_BOTH: case P_IMUL+POF_BOTH:
case P_AND+POF_BOTH: case P_XOR+POF_BOTH: case P_IOR+POF_BOTH:
case P_AOS: case P_SOS:
case P_HRRM: case P_HRLM:
case P_MOVN+POF_BOTH: case P_MOVE+POF_BOTH:
case P_SETZ+POF_BOTH: case P_SETO+POF_BOTH: case P_SETCM+POF_BOTH:
if ((q->Ptype & PTF_ADRMODE) != PTA_MINDEXED) {
usedreg |= rbuse(q);
continue;
}
if (q->Preg == p->Pindex
|| sameaddr(p, q, stkoff) || alias (p, q, stkoff))
return;
usedreg |= rbinreg(q);
continue;
/* Some op type we don't know?
** Jumps, in particular, come here to simply return and give up the
** optimization attempt.
*/
default:
return;
}
}
/* FOLDPLUS(p) - Improve instruction used to do addition.
**
** takes an addition operation (P_ADD, P_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 == P_ADD && p->Ptype == PTA_REGIS /* !prevskips */ &&
(q = before(p)) != NULL && q->Preg == p->Pr2 && !prevskips (q))
switch (q->Pop) {
case P_SUB:
if (q->Ptype == PTV_IMMED) {
q->Pop = P_ADD; /* make P_SUBI R,i into P_ADDI R,-i */
q->Pvalue = - q->Pvalue; /* so it can be folded later */
} /* in any case treat like P_ADD here */
case P_ADD:
/*
** fold: P_ADD S,x
** P_ADD R,S
**
** into: P_ADD R,S
** P_ADD R,x
*/
if ((q->Ptype&PTF_ADRMODE) == PTA_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 == P_MOVE && q->Ptype == PTA_REGIS &&
q->Preg == p->Preg) {
/*
** fold: P_MOVE R,S
** P_ADD R,x
**
** into: P_ADD S,x
** P_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 P_ADD, P_SUB */
case P_AOS:
case P_SOS:
case P_MOVE:
case P_MOVN:
if (((q->Ptype & PTF_ADRMODE) == PTA_MINDEXED
&& q->Pindex == p->Preg) ||
(q->Ptype & PTF_IND) || (b = before(q)) == NULL ||
((r = rchange (b->Pop)) != PRC_RCHG && r != PRC_RSET) ||
b->Preg != p->Preg || !(b->Ptype & PTF_IMM) || prevskips (b) ||
((b->Ptype & PTF_ADRMODE) == PTA_MINDEXED && q->Preg == b->Pindex)) break;
/*
** fold: P_ADDI R,x
** P_AOS S,y
** P_ADD R,S
**
** into: P_AOS S,y
** P_ADDI R,x
** P_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 == P_ADD) {
switch (q->Pop) {
case P_MOVN: case P_SUB:
case P_SETO: case P_SETZ:
if (!unsetz (q)) break; /* turn into P_MOVE or P_ADD */
case P_MOVE:
case P_ADD:
switch (p->Ptype) {
case PTV_IMMED:
switch (q->Ptype) {
case PTV_IINDEXED:
case PTV_IMMED:
/*
** fold: P_MOVEI R,addr(i)
** P_ADDI R,c
**
** into: P_MOVEI R,addr+c(i)
*/
q->Poffset += p->Pvalue;
p->Pop = P_NOP; /* poof! */
fixprev();
p = q;
continue;
case PTA_REGIS:
if (q->Pop == P_MOVE) {
/*
** fold: P_MOVE R,S
** P_ADDI R,x
**
** into: P_ADDI S,x
** P_MOVE R,S
*/
if ((q->Ptype&PTF_ADRMODE) == PTA_MINDEXED
&& q->Preg == q->Pindex)
return;
swappseudo(p, q); /* swap the ops */
r = q->Preg = p->Pr2; /* fix reg in P_ADDI */
p = q; /* move back over it */
}
default: if (q->Pop == P_ADD) continue;
} /* end switch(q->Ptype) */
break; /* propagate break to escape loop */
case PTV_IINDEXED:
switch (q->Ptype) {
case PTV_IINDEXED:
if ((q->Pptr != NULL && p->Pptr != NULL) ||
(q->Pindex != 0 && p->Pindex != 0)) continue;
case PTV_IMMED:
/*
** fold: P_MOVEI R,c
** P_ADDI R,addr(i)
**
** into: P_MOVEI R,addr+c(i)
*/
p->Pop = q->Pop;
q->Pop = P_NOP;
p->Poffset += q->Poffset;
if (q->Ptype == PTV_IINDEXED) {
if (p->Pptr == NULL) p->Pptr = q->Pptr;
if (p->Pindex == 0) p->Pindex = q->Pindex;
}
continue;
case PTA_REGIS:
if (q->Pop == P_MOVE) {
/*
** fold: P_MOVE R,S
** P_ADDI R,x
**
** into: P_ADDI S,x
** P_MOVE R,S
*/
swappseudo(p, q); /* swap the ops */
r = q->Preg = p->Pr2; /* fix reg in P_ADDI */
p = q; /* move back over it */
}
continue;
default:
if ((q->Ptype&PTF_ADRMODE) == PTA_MINDEXED
&& q->Preg == q->Pindex)
return;
if (q->Pop != P_MOVE || p->Pindex == 0) continue;
p->Pop = q->Pop; /* turn P_MOVE+XP_ADDI into */
q->Pop = P_ADD; /* P_XMOVEI+P_ADD to save one */
swappseudo(p, q); /* instruction... */
p = q; /* look at correct one next loop */
}
continue; /* fall through goes back */
case PTA_REGIS:
if (q->Ptype == PTV_IINDEXED && q->Pindex == 0) {
/*
** fold: P_XMOVEI R,x
** P_ADD R,S
**
** into: P_XMOVEI R,x(S)
*/
q->Pindex = foldcse (p->Pr2, before (q)); /* new index */
p->Pop = P_NOP; /* drop now useless move */
fixprev(); /* maybe that was last instr in buf */
return; /* not likely any more to do */
}
if ((q->Ptype & PTF_ADRMODE) == PTA_MINDEXED
&& q->Pindex == r) {
if (!(q->Ptype & PTF_IMM) || q->Pop != P_MOVE) break;
/*
** fold: P_XMOVEI R,x(R)
** P_ADD R,S
**
** into: P_ADD R,S
** P_XMOVEI R,x(R)
**
** (avoid silly checks and opcode switches below
*/
} else if (q->Ptype == PTA_REGIS && q->Pop == P_MOVE) {
/*
** fold: P_MOVE R,S
** P_ADD R,T
**
** into: P_ADD S,T
** P_MOVE R,S
*/
if (q->Pr2 == R_SP) return; /* don't break stack */
p->Preg = q->Pr2; /* set register */
} else {
/*
** fold: P_MOVE R,x
** P_ADD R,S
**
** into: P_MOVE R,S
** P_ADD R,x
*/
if ((q->Ptype&PTF_ADRMODE) == PTA_MINDEXED
&& q->Preg == q->Pindex)
return;
p->Pop = q->Pop; /* set opcode */
q->Pop = P_ADD; /* to swap them */
}
swappseudo(p,q); /* opcodes hacked, swap ops */
foldplus (q); /* try again lower down */
if (p->Ptype == PTA_REGIS) return; /* avoid infinite loop */
q = p; /* start after the swap */
continue; /* try loop again */
default:
switch (q->Ptype) {
case PTV_IINDEXED:
if (q->Pindex != 0) continue; /* bad swap */
case PTV_IMMED: /* swap order of immed and */
p->Pop = q->Pop; /* memory P_MOVE/P_ADD so that */
q->Pop = P_ADD; /* P_ADDI can be optimized */
swappseudo(p, q); /* even further. */
break; /* not likely to find any more */
case PTA_REGIS:
if (q->Pop == P_MOVE) {
/*
** fold: P_MOVE R,S
** P_ADDI R,x
**
** into: P_ADDI S,x
** P_MOVE R,S
*/
swappseudo(p, q); /* swap the ops */
r = q->Preg = p->Pr2; /* fix reg in P_ADDI */
p = q; /* move back over it */
} /* end PTA_REGIS: if (q->Pop==P_MOVE) */
default: if (q->Pop == P_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 &~ (PTF_IND + PTF_SKIPPED)) {
case PTV_IMMED:
/*
** fold: P_ADDI R,[-n]
** into: P_SUBI R,n
*/
if (p->Pvalue < 0) {
switch (p->Pop) {
case P_ADD:
p->Pop = P_SUB;
break;
case P_MOVE:
p->Pop = P_MOVN;
break;
case P_SUB:
p->Pop = P_ADD;
break;
case P_MOVN:
p->Pop = P_MOVE;
break;
default: return;
}
p->Pvalue = - p->Pvalue;
} else if (p->Pvalue == 0) {
switch (p->Pop) {
case P_ADD:
case P_SUB:
p->Pop = P_NOP; /* fold: P_ADDI R,0 */
fixprev(); /* into: nothing */
return;
case P_MOVN:
case P_MOVE:
p->Pop = P_SETZ; /* fold: P_MOVEI R,0 */
p->Ptype = PTA_ONEREG; /* into: P_SETZ R, */
return;
}
}
if (p->Pvalue == 1 && p->Pop == P_MOVN) {
p->Pop = P_SETO; /* fold: P_MOVNI R,1 */
p->Ptype = PTA_ONEREG; /* into: P_SETO R, */
return;
}
break;
case PTV_IINDEXED:
if (p->Pop == P_ADD && p->Pindex == 0) {
/*
** fold: P_ADDI R,addr
** into: P_MOVEI R,addr(R)
*/
p->Pop = P_MOVE; /* make P_XMOVEI */
p->Pindex = p->Preg; /* with index same as reg */
if ((q = before(p)) != NULL && q->Ptype == PTA_REGIS
&& q->Pop == P_MOVE
/* !prevskips (p or q) */ && q->Preg == p->Pindex) {
/*
** fold: P_MOVE R,S
** P_MOVEI R,addr(R)
**
** into: P_MOVEI R,addr(S)
*/
p->Pindex = q->Pr2; /* flatten index calculation */
q->Pop = P_NOP; /* flush useless P_MOVE */
}
p->Pindex = foldcse(p->Pindex, before(p)); /* fold idx */
}
break;
case PTA_MINDEXED:
if (p->Pop != P_ADD+POF_BOTH || (q = before (p)) == NULL ||
prevskips (p) || prevskips (q) || q->Preg != p->Preg) break;
if (unsetz(q)) {
if (q->Pop != P_MOVE || q->Ptype != PTV_IMMED) {
foldplus(q); /* Restore more efficient op */
break; /* (ie undo what unsetz did) */
}
/* Previous instr should now be a P_MOVEI */
switch (q->Pvalue) {
case -1:
q->Pop = P_NOP; /* fold: P_MOVNI R,1 */
p->Pop = P_SOS; /* P_ADDB R,x */
foldinc (p); /* into: P_SOS R,x */
break;
case 0:
q->Pop = P_NOP; /* fold: P_MOVEI R,0 */
p->Pop = P_MOVE; /* P_ADDB R,x */
foldmove(p); /* into: P_MOVE R,x */
break;
case 1:
q->Pop = P_NOP; /* fold: P_MOVEI R,1 */
p->Pop = P_AOS; /* P_ADDB R,x */
foldinc (p); /* into: P_AOS R,x */
break;
default:
foldplus (q); /* put back more efficient op */
}
}
}
}
/* FOLDBYTE(p) - Optimize P_ADJBP instruction.
**
** takes an P_ADJBP instruction in the peephole buffer,
** and tries to fold it out (e.g. by turning it into an P_ADDI).
*/
void
foldbyte (p)
PCODE *p;
{
PCODE *q, *n;
int r, s;
int a, boff, woff, bsiz;
/* Make sure we're looking at an unskipped P_ADJBP */
if (p->Pop != P_ADJBP || prevskips (p) || !optobj) {
foldmove (p); /* just run cse */
return;
}
/*
** Find the instruction that sets R.
*/
if (rinaddr(p, p->Preg)) /* Make sure R not otherwise used in ADJBP! */
return;
if ((n = findrset(before(p), p->Preg)) == NULL) {
foldmove(p); /* Nothing to fold into, try cse */
return;
}
/*
** Found it, 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 P_SETZ:
n->Pop = P_NOP;
case P_SETZ+POF_BOTH:
n->Preg = R_SCRREG; /* take out reg if not whole op */
p->Pop = P_MOVE; /* change null ADJBP into MOVE */
foldmove(p); /* try further optimizations */
return; /* all done */
case P_MOVN: case P_SUB: case P_SETO: case P_MOVE: case P_ADD:
if (unsetz (n) && n->Ptype == PTV_IMMED) break;
default:
foldmove(p);
return;
}
/* Now if instr is an ADJBP R,S (register-register) then we may be
** able to do further optimizations depending on how S is set.
*/
q = NULL; /* Initialize, no byte-pointer construction */
if (p->Ptype == PTA_REGIS /* If ADJBP R,S then */
&& (q = findrset(before(p), p->Pr2)) /* find instr that sets S */
&& q->Pop == P_MOVE) { /* Check for simple setup */
/* Change MOVE S,x /.../ ADJBP R,S into ADJBP R,x
** Note that x may be a pointer constant, which allows following
** optimization to win big.
*/
r = p->Preg; /* Save R */
*p = *q; /* Copy the MOVE instruction onto ADJBP */
p->Pop = P_ADJBP; /* Make it an ADJBP again */
p->Preg = r; /* with proper register */
q->Pop = P_NOP; /* and flush old MOVE instruction */
q = NULL; /* Say no byte-pointer construct instr */
}
/* Check for ADJBP R,[pconst].
** We can simply fiddle with the pointer-constant parameters to
** accomplish the effect of the ADDI/MOVEI that n points to.
** If it was a MOVEI (thus completely determining the new pointer)
** then the ADJBP can be flushed and replaced with a MOVE!
*/
if (p->Ptype == PTA_PCONST) {
p->Poffset += n->Pvalue; /* Add together the byte offsets */
if (n->Pop == P_MOVE) /* Was R set by a MOVEI? */
p->Pop = P_MOVE; /* Yes, turn ADJBP R,x into MOVE R,x */
n->Pop = P_NOP; /* Now flush the ADDI or MOVEI */
return; /* Win, win! */
}
/* Now see if there is a byte-pointer construction instruction
** before an ADJBP R,S in which case we may also be able to flush
** the ADJBP (and a MOVEI if that is what sets R) by changing the
** way the pointer is constructed.
*/
if (q && q->Pop == P_IOR /* Check for IOR S,[pconst] */
&& q->Ptype == PTA_PCONST
&& q->Pptr == NULL) { /* Better not have any symbol! */
/* If found a byte-pointer construction:
**
** fold: n-> MOVEI/ADDI R,n
** ...
** q-> IOR S,[pconst + byteoffset]
** ...
** p-> ADJBP R,S
**
** into: ADDI S,i (can flush if i == 0)
** IOR S,[pconst + new byteoffset]
** MOVE/ADJBP R,S (MOVE if n was MOVEI; else ADJBP)
*/
bsiz = q->Pbsize; /* Save bytesize */
boff = adjboffset(n->Pvalue+q->Poffset, /* Get sum of byte offsets */
&woff, /* and set woff/boff */
(TGSIZ_WORD/bsiz));
a = n->Pop; /* Remember the op that set R */
r = p->Preg; /* Remember regs of P_ADJBP for later */
s = p->Pr2;
/* Flush old instructions */
n->Pop = P_NOP; /* Flush MOVEI/ADDI */
q->Pop = P_NOP; /* Flush IOR */
p->Pop = P_NOP; /* Flush ADJBP */
fixprev();
/* Make new instructions */
if (woff) code1(P_ADD, s, woff); /* New ADDI */
code10(P_IOR, s, (SYMBOL *)NULL, bsiz, boff); /* New IOR */
code0( (a==P_MOVE ? P_MOVE : P_ADJBP), r, s); /* New MOVE or ADJBP */
return;
}
/* That lost. Last possibility to check for is simply adding 1 to the
** pointer, which can be done with a simple IBP.
*/
if (n->Pop == P_MOVE && n->Pvalue == 1) {
n->Pop = P_NOP; /* change P_MOVEI R,1 + P_ADJBP R,x */
p->Pop = P_MOVE; /* into P_MOVE R,x + P_IBP R */
r = p->Preg;
foldmove(p); /* optimize the P_MOVE */
/* make P_IBP without optimization */
p = newcode();
p->Ptype = PTA_REGIS;
p->Pop = P_IBP;
p->Preg = 0;
p->Pr2 = r;
return;
}
foldplus(n); /* all optimization failed, fix up the # */
foldmove(p); /* and try to improve the ADJBP memory reference */
}
/* FINDRSET(p, r) - Find the instruction that sets R, searching backwards
** starting at P. It is OK to attempt starting at NULL; this
** facilitates constructs like findrset(before(p),r).
**
** NOTE: the instruction found may not actually SET the register
** or alter its value; it only USES it in its AC field. Thus the caller
** must apply further checks on the exact opcode.
**
** The instr is assumed to be the most recent one with the register
** field set to R, provided R is not referenced in any way by intervening
** instructions. This includes being used in an address calculation,
** or being the 2nd reg of a double-word op, or finding a PUSHJ.
** Note that the instruction found may be either a double-word or
** single-word op. Also, we don't bother checking the skipped flag while
** moving back, as whether an instr is skipped is irrelevant to whether it
** might use or set the register in question. The instr found (if any) is
** checked for this, however.
*/
PCODE *
findrset(p, reg)
PCODE *p; /* Start looking here */
int reg; /* Register to look for */
{
/* Loop until break out or no instrs left */
for (; p; p = before(p)) {
switch (rinreg(p, reg)) {
default: /* Unknown changes, give up */
return NULL;
case 0: /* Not used in this instr, continue loop */
break;
case 1: /* Win, found single-word op that uses it */
case 2: /* Win, found double-word op that uses it */
/* Found an op that uses this register! Done, unless this instr
** might be skipped over (in which case we can't be sure what it
** will contain and must give up).
*/
if (prevskips(p)) /* Is prev instr a skip? */
return NULL; /* Sigh */
return p; /* Won, found instr that sets reg! */
}
if (rinaddr(p, reg)) /* If reg used in address somehow, */
return NULL; /* stop looking immediately */
}
return NULL; /* No more instrs to look at */
}
/* FINDCONST(p) - Turn register to register op into register immediate.
** Looks back through peephole buffer for op setting second
** register to a constant value. If found, replaces register
** with that in the given operation.
** Only called by rrpop2() in CCCODE, after instruction added to buffer.
*/
void
findconst (p)
PCODE *p;
{
PCODE *q;
int op, rused;
if (p->Ptype != PTA_REGIS) return; /* Verify; must be reg-reg operation */
rused = 0; /* Clear flag */
for (q = before (p); q != NULL; q = before (q)) { /* look back in buf */
switch (q->Pop) { /* see what op is */
case P_MOVE: /* Move, */
case P_CAI+POF_ISSKIP+POS_SKPE: /* or compare acting like move */
if ((q->Ptype & PTF_ADRMODE) != PTA_RCONST)
break; /* must be immediate */
case P_SETZ: /* Zero makers */
case P_JUMP+POS_SKPN:
case P_AOJ+POS_SKPN: case P_SOJ+POS_SKPN:
case P_SKIP+POF_ISSKIP+POS_SKPE:
if (q->Preg != p->Pr2) break; /* but not the reg we want */
if ((isskip (q->Pop) || prevskips (q)) && !dropsout (after (q)))
break; /* or skips, or skipped */
/* Won! Found constant value for this reg */
p->Ptype = PTV_IMMED; /* set immediate instruction */
if (op = immedop (p->Pop)) { /* except for P_CAM */
p->Pop = op; /* which becomes P_CAI */
p->Ptype = PTA_RCONST; /* with constant type */
}
p->Pvalue = (q->Ptype & PTF_ADRMODE) == PTA_RCONST
? q->Pvalue : 0; /* Set to value or zero */
if (q->Pop == P_MOVE && rused == 0) /* If simple set, and safe, */
q->Pop = P_NOP; /* Simply flush the setting instr. */
return; /* made const, now done */
}
/* Not found, make sure reg not munged */
if (rmodreg(q, p->Pr2)) return; /* Return if reg modified */
/* See if our reg is used for anything within an instruction,
** so we know whether it's OK to flush the
** instruction that sets it (if we find one)
*/
switch (p->Ptype & PTF_ADRMODE) {
case PTA_MINDEXED: /* These two use index reg */
case PTA_BYTEPOINT:
if ((p->Pindex == q->Pindex)
|| (p->Pindex == q->Preg)) rused++;
break;
case PTA_REGIS:
if (p->Pr2 == q->Pr2) rused++; /* Drop thru */
default:
if (p->Pr2 == q->Preg) rused++;
}
}
}
/* FOLDBOTH() - Make ops of type POF_BOTH.
**
** looks at the last instruction in the peephole buffer,
** and if it is a MOVEM tries to fold it into an opB.
**
** KLH: This is yet another BIG MESS that badly needs to be cleaned up
** someday!!
*/
void
foldboth()
{
PCODE *p, *b, *q;
int badidx, r, s;
static int snglop();
foldmove(previous); /* maybe it's a P_MOVE or P_LDB */
if (prevskips (previous)) return; /* skipped over, can't hack */
if (previous->Pop == P_ADD+POF_BOTH) /* try += into ++ */
foldplus (previous);
/*
** fold: P_ADJSP 17,n
** ...
** P_MOVEM R,1-n(17)
**
** into: ...
** P_PUSH 17,R
** P_ADJSP 17,n-1
*/
if ( previous->Pop == P_MOVEM
&& previous->Ptype == PTA_MINDEXED
&& previous->Pindex == R_SP) /* if P_MOVEM onto stack */
for (p = before (previous); p != NULL; p = before (p)) /* looking back */
switch (p->Pop & POF_OPCODE) { /* through opcodes... */
case P_JRST: case P_JUMP:
case P_AOJ: case P_SOJ: /* control flow mungage */
case P_PUSH: case P_POP: case P_POPJ: /* or stack mungage */
p = NULL; /* give up */
break;
case P_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 == R_SP) switch (q->Ptype & PTF_ADRMODE) {
case PTA_MINDEXED: case PTA_BYTEPOINT: /* indexed types */
q->Poffset += p->Pvalue; /* adjust for hacked stack */
}
swappseudo (p, q); /* bubble P_ADJSP forward */
p = q; /* point to where it is now */
}
swappseudo (p, previous); /* now swap P_MOVEM and P_ADJSP */
p->Pop = P_PUSH; /* make P_PUSH in place of P_MOVEM */
p->Ptype = PTA_REGIS; /* this is now a register op */
p->Pr2 = p->Preg; /* with second reg old first */
p->Preg = R_SP; /* and first reg stack */
if ((-- previous->Pvalue) == 0) { /* diminish P_ADJSP by one */
previous->Pop = P_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 P_SETZ: case P_SETO: case P_MOVN:
if (!unsetz (q)) break; /* turn P_SETZ into P_MOVE */
case P_MOVE:
/*
** fold: P_MOVE R,x
** P_PUSH 17,R
**
** into: P_PUSH 17,x
** P_MOVE R,0(17)
**
** in hope that genrelease() will flush the P_MOVE.
*/
if (q->Ptype == PTV_IINDEXED) break; /* no XP_PUSHI */
p->Pop = P_MOVE; /* get different P_MOVE instr */
p->Ptype = PTA_MINDEXED; /* from memory, no indirection */
p->Preg = q->Preg; /* into destination register */
p->Pindex = R_SP; /* from stack */
p->Pptr = 0; /* not global variable space */
p->Poffset = 0; /* top of stack */
q->Pop = P_PUSH; /* now make old P_MOVE into P_PUSH */
q->Preg = R_SP; /* onto stack */
}
return; /* no more to do here */
default:
switch (p->Ptype & PTF_ADRMODE) {
case PTA_MINDEXED:
case PTA_BYTEPOINT:
if (p->Pindex == R_SP) {
if (p->Poffset < previous->Poffset)
continue; /* arg, safe */
p = NULL; /* Not safe */
}
break;
case PTA_REGIS:
if (p->Pr2 == R_SP)
p = NULL; /* OP R,17 loses big */
break;
}
}
/*--------------------------------------*/
if ((b = before(previous)) != NULL
&& b->Ptype == PTA_REGIS /* !prevskips */
&& b->Pop == P_MOVE && b->Preg == previous->Preg
&& snglop(previous->Pop)) { /* Single-word op */
/*
&& p->Pop != P_IDIV && p->Pop != P_UIDIV) {
*/
/* BUG! This call is known to swap an IDIVI and zap its reg!
*/
/*
** fold: P_MOVE R,S
** OP R,x
**
** into: OP S,x
** P_MOVE R,S
*/
previous->Preg = b->Pr2; /* flatten the register change */
swappseudo(b, previous); /* switch the two ops */
p = b; /* forget about the P_MOVE */
} else b = p = previous; /* no move, start at the top */
if (p->Ptype != PTA_MINDEXED) return;
switch (p->Pop) {
case P_AOS:
case P_SOS:
#if 0 /* BAD STUFF! KLH: This code loses (and has caused actual lossage in
** compiled code of the form "++I * ++I") because it generates a
** MOVE S,R which causes the peepholer to think that it is safe
** to throw R away completely -- which is NOT what the original code
** wanted to do or imply!
*/
if ((b = before (p)) != NULL && b->Pop == P_MOVEM &&
b->Ptype == PTA_MINDEXED /* !prevskips */ && b->Pptr == p->Pptr &&
b->Pindex == p->Pindex && b->Poffset == p->Poffset) {
/*
** fold: P_MOVEM R,x
** P_AOS S,x
**
** into: P_ADDI R,1
** P_MOVEM R,x
** P_MOVE S,R
*/
b->Pop = P_ADD; /* make P_ADD */
b->Ptype = PTV_IMMED; /* immediate quantity */
b->Pvalue = (p->Pop == P_AOS)? 1 : -1; /* by one */
s = p->Preg; /* remember the reg we want */
p->Pop = P_MOVEM; /* now make P_MOVEM */
r = p->Preg = b->Preg; /* using old register */
foldplus(b); /* fix up addition */
code0 (P_MOVE, s, r); /* and fix up registers */
} else
#endif /* End of bad stuff */
foldinc (p);
default: return;
case P_MOVEM: break;
}
/*
** At this point we know that p = previous points to a P_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 & PTF_ADRMODE) == PTA_MINDEXED
&& b->Pindex == p->Preg) return;
if (b->Preg == p->Pindex) badidx = 1; /* can't fold unless P_SETZ */
switch (rchange (b->Pop)) {
case PRC_RSAME: /* nice single word op? */
case PRC_RSET:
case PRC_RCHG:
case PRC_RCHG_DSAME:
if (b->Preg == p->Preg) break; /* the one we want */
continue; /* else look back some more */
case PRC_DSAME: /* nasty double word op? */
case PRC_DSET:
case PRC_DSET_RSAME:
case PRC_DCHG:
case PRC_DCHG_RSAME:
if (!prevskips (b) && (b->Pop == P_DFMP || b->Pop == P_DFDV) &&
b->Preg == p->Preg && (q = before (b)) != NULL &&
!prevskips (q) && q->Pop == P_SETZ && q->Preg == p->Preg + 1) {
/*
** fold: P_SETZ R+1
** P_DFMP R,x (or DFDV R,x)
** P_MOVEM R,y
**
** into: P_FMPR R,x (or FDVR R,x)
** P_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 == PTA_ONEREG) q->Pop = P_NOP; /* drop P_SETZ */
b->Pop = ((b->Pop == P_DFMP)? P_FMPR : P_FDVR); /* make singleword */
/* Fix up operand if a constant */
if ((b->Ptype&PTF_ADRMODE) == PTA_DCONST) {
b->Ptype = (b->Ptype& ~PTF_ADRMODE) | PTA_FCONST;
b->Pfloat = (float)b->Pdouble;
}
break; /* escape loop */
}
if (b->Preg - 1 == p->Pindex) badidx = 1;
if (b->Preg != p->Preg && b->Preg - 1 != p->Preg) continue;
default: /* P_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 P_FADR: case P_FSBR: case P_FDVR: case P_FMPR:
case P_ADD: case P_IMUL: case P_SUB: case P_MOVE:
case P_MOVN: case P_SETZ: case P_SETO: case P_FLTR:
case P_FIX: case P_AND: case P_IOR: case P_SETCM:
q = before (q);
continue;
}
break; /* break out with unknown op */
}
switch (b->Pop) {
case P_FMPR:
case P_IMUL:
if (q != NULL && q->Pop == P_MOVN && q->Preg == p->Preg && !badidx &&
q->Ptype == p->Ptype /* !prevskips */ && q->Pptr == p->Pptr &&
q->Poffset == p->Poffset && q->Pindex == p->Pindex) {
/*
** fold: P_MOVN R,x
** P_IMUL R,y
** P_MOVEM R,x
**
** into: P_MOVN R,y
** P_IMULB R,x
*/
p->Pop = b->Pop | POF_BOTH; /* move mply op over */
b->Pop = P_MOVN; /* make old add into negated move */
q->Pop = P_NOP; /* flush the first move */
foldplus(p); /* maybe we can turn it into an P_AOS */
break;
}
case P_FADR: case P_FSBR:
case P_ADD: case P_SUB:
case P_AND: case P_IOR: case P_XOR:
if (q != NULL && q->Pop == P_MOVE && q->Preg == p->Preg && !badidx &&
q->Ptype == p->Ptype /* !prevskips */ && q->Pptr == p->Pptr &&
q->Poffset == p->Poffset && q->Pindex == p->Pindex) {
/*
** fold: P_MOVE R,x
** P_ADD R,y
** P_MOVEM R,x
**
** into: P_MOVE R,y
** P_ADDB R,x
*/
if (b->Pop == P_SUB) {
p->Pop = P_ADD+POF_BOTH; /* make P_ADD */
b->Pop = P_MOVN; /* and P_MOVN */
} else if (b->Pop == P_FSBR) {
p->Pop = P_FADR+POF_BOTH; /* make floating add */
b->Pop = P_MOVN; /* and floating P_MOVN (same as int) */
} else {
p->Pop = b->Pop | POF_BOTH; /* move add op over */
b->Pop = P_MOVE; /* make old add into move */
}
q->Pop = P_NOP; /* flush the first move */
if (b->Ptype == PTA_REGIS
&& (b->Pop == P_MOVE || pushneg(b->Pr2, before (b)))) {
b->Pop = P_NOP;
p->Preg = b->Pr2;
code0 (P_MOVE, b->Preg, b->Pr2);
}
foldplus(p); /* maybe we can turn it into an P_AOS */
break;
}
case P_MOVE: case P_MOVN: case P_MOVM: case P_SETCM:
case P_FDVR: case P_IDIV: /* yes, P_IDIVB works */
if (b->Ptype == p->Ptype && b->Poffset == p->Poffset &&
b->Pindex == p->Pindex && b->Pptr == p->Pptr && !badidx) {
/*
** fold: OP R,addr
** P_MOVEM R,addr
**
** into: OPB R,addr
*/
p->Pop = b->Pop; /* move opcode across */
if (b->Pop != P_MOVE) p->Pop |= POF_BOTH;
b->Pop = P_NOP; /* flush old instruction */
foldplus(p); /* maybe we can turn it into an P_AOS */
}
break;
case P_SETZ:
case P_SETO:
if (b->Ptype == PTA_ONEREG) {
/*
** fold: P_SETZ R,
** P_MOVEM R,x
**
** into: P_SETZB R,x
*/
p->Pop = b->Pop+POF_BOTH; /* move opcode across, make both */
b->Pop = P_NOP; /* flush old instruction */
}
}
}
static int
snglop(op)
int op;
{
switch (rchange(op)) {
case PRC_RSAME:
case PRC_RSET:
case PRC_RCHG:
case PRC_RCHG_DSAME:
return 1; /* OK single-word operation */
default:
return 0; /* Anything else is assumed not OK */
}
}
/* FOLDSTACK(n) - Optimize previous instructions into P_ADJSP.
**
** Attempts to pull previous P_ADJSPs, P_PUSHs, etc into a new P_ADJSP
** which would, if no such optimization occurred, have constant n.
** The return value is what the constant should be, taking into
** account any optimization which has been done.
** This is only called by code8 in CCCODE when about to generate
** an ADJSP. This is also invoked via code8 when code0 generates
** a PUSH P,R.
** It is ALWAYS safe to flush an ADJSP P,-n because that just
** leaves some extra junk on the stack and doesn't invalidate any
** following references.
** The thing to beware of is flushing positive adjustments
** (ADJSP P,+n) because this could leave following code referencing
** places beyond the end of the stack (i.e. indexing through P with
** a positive offset), unless we are careful and
** have made sure that no such references exist.
** The "maxfold" variable is to ensure that we don't accidentally
** do this. It is set to the largest positive N that it is safe to
** flush (if an ADJSP P,+N is encountered); this means that no code
** has been seen which references a location higher than -maxfold(P).
** That is, -maxfold(P) is assumed to have a reference.
** Note that 0(P) is a valid reference. maxfold should never be
** negative!
** Finally, if we know from "stackrefs" that the current function
** generates an address pointing to something on the stack, then it is
** never safe to flush a positive ADJSP, since we can't tell just from
** the instruction memory references whether a portion of the stack is
** really being used or not. This is why maxfold is set to 0 in that
** case, which means that as far as is known, all of the current stack is
** referenced.
*/
int
foldstack(n)
{
PCODE *p = previous; /* Start from end of buffer */
int maxfold;
maxfold = (stackrefs ? 0 : 1000000); /* limit on pos ADJSP fold */
while (p != NULL) switch (p->Pop & POF_OPCODE) {
case P_ADJSP:
/* See if safe to merge this ADJSP in with current one (at end),
** and if so adjust stack to account for its flushage, and
** then flush it.
*/
if (p->Preg != R_SP /* Must be ADJSP 17, */
|| prevskips(p) /* and not skipped over */
|| p->Pvalue > maxfold) /* and not about to invalidate refs */
return n; /* Failed one of above, quit. */
n += adjstack(p->Pvalue, p); /* Won, adjust all following refs */
n += p->Pvalue;
maxfold -= p->Pvalue;
flspop(p); /* Flush that ADJSP */
p = previous; /* Start loop over */
break;
case P_PUSH:
/* OK to look past a PUSH P,[0]/[-1] because
** adjstack() knows how to convert those to a SETZB/SETOB -n(P)
** which is more efficient.
** Likewise, PUSH P,R is ok because adjstack() can change it to
** a MOVEM R,-n(P).
** A PUSH of anything else would
** take 2 instructions to replace the PUSH if the stack pointer
** was different, so it does us no good to continue looking back.
*/
if (p->Preg != R_SP) return n;
if (((p->Ptype == PTV_IMMED && p->Pvalue >= -1 && p->Pvalue <= 0)
|| p->Ptype == PTA_REGIS) /* && !prevskips(p) */) {
maxfold = 0; /* This referenced top of stack */
p = before(p);
break; /* Continue looking back */
}
return n;
case P_POP:
/* OK to look past a POP if it is a POP P,R
** since adjstack() can replace that with MOVE R,-n(P) if the
** stack pointer is changed.
** Otherwise, as for PUSH, we stop since it would take 2 instrs
** (a MOVE and MOVEM) to replace the POP; settle for what we have
** now.
*/
if ( p->Preg == R_SP /* Must be POP 17, */
&& p->Ptype == PTA_REGIS /* and a POP 17,R */
/* && !prevskips(p) */ ) { /* and not skipped over */
maxfold = 0; /* This is a ref to top of stack */
p = before(p); /* continue looking back */
break;
}
return n;
#if 0 /* Should never have positive index to stack like this! */
case P_MOVEM:
if (p->Ptype == PTA_MINDEXED
&& p->Pindex == R_SP
&& p->Poffset == 1 && n > 0 && !prevskips (p)) {
/*
** fold: P_MOVEM R,1(17)
** into: P_PUSH 17,R
*/
p->Ptype = PTA_REGIS;
p->Pr2 = p->Preg;
p->Preg = R_SP;
p->Pop = P_PUSH;
n += adjstack(-1, p);
n--;
p = previous;
maxfold = (stackrefs? 0 : 1000000); /* start again */
break;
}
/* Can't fold, check for stack ref */
if (( (p->Ptype & PTF_ADRMODE) == PTA_MINDEXED
||(p->Ptype & PTF_ADRMODE) == PTA_BYTEPOINT)
&& p->Pindex == R_SP
&& p->Poffset > - maxfold)
maxfold = - p->Poffset;
p = before(p);
break;
#endif
case P_MOVE:
if ( p->Ptype == PTA_MINDEXED /* && !prevskips(p) */
&& p->Pindex == R_SP
&& p->Poffset == 0 /* MOVE R,0(17) */
&& n < 0 /* Want stuff popped off */
&& (maxfold > 0 /* No other ref to top of stack */
|| p == previous)) { /* or no other instrs in between */
/*
** fold: P_MOVE R,0(17)
** into: P_POP 17,R
*/
n += makepop(p); /* turn into P_POP */
p = previous;
maxfold = (stackrefs ? 0 : 1000000); /* Start again */
break;
}
/* Can't fold, drop thru to normal check for stack ref */
case P_MOVEM:
case P_ADD: case P_SUB: case P_IMUL: case P_IDIV: case P_UIDIV:
case P_FADR: case P_FSBR: case P_FDVR: case P_FMPR:
case P_AND: case P_IOR: case P_XOR: case P_CAI:
case P_MOVN: case P_SETCM: case P_SETZ: case P_CAM:
case P_SETO: case P_AOS: case P_SOS: case P_LSH:
case P_ADJBP: case P_SUBBP: case P_IBP:
case P_ILDB: case P_IDPB: case P_LDB: case P_DPB:
case P_HLRZ: case P_HRLM: case P_HRRZ: case P_HRRM:
case P_TDC: case P_TDN: case P_TDO: case P_TDZ:
case P_TLC: case P_TLN: case P_TLO: case P_TLZ:
case P_TRC: case P_TRO: case P_TRN: case P_TRZ:
case P_SKIP:
case P_SMOVE:
/* If instr is one we understand, can check it for stack ref */
if ( ((p->Ptype & PTF_ADRMODE) == PTA_MINDEXED
||(p->Ptype & PTF_ADRMODE) == PTA_BYTEPOINT)
&& p->Pindex == R_SP /* If indexing through stack ptr */
&& p->Poffset > -maxfold) /* See if reference is higher on stk */
maxfold = - p->Poffset; /* Yes, remember this as highest ref */
p = before(p);
break;
default:
/* Instr we don't understand, don't try to optimize back over it */
return n;
}
return n;
}
/* MAKEPOP(p) - Turn P_MOVE instruction into P_POP
**
** This is only called by foldstack().
** This is the only place in KCC where a P_POP pseudo-code instruction
** is generated.
**
** This turns the given P_MOVE R,0(17) into P_POP 17,R and fixes up
** the surrounding instructions. It returns the result of
** adjstack() + 1 (for the new extra pop).
**
** WARNING! This code makes several dangerous assumptions about the
** way PCODE members overlap! -- disgustedly, KLH
*/
static int
makepop(p)
PCODE *p;
{
PCODE *q, *b;
/* first make the pop */
p->Pop = P_POP;
p->Ptype = PTA_REGIS;
p->Pr2 = p->Preg; /* Copy R into 2nd reg */
p->Preg = R_SP;
/* now see if we can further fold it */
if (p->Pr2 != R_RETVAL && (q = after (p)) != NULL && q->Pop == P_MOVEM &&
q->Preg == p->Pr2) {
for (b = q; b != NULL && q != NULL; b = after (b)) {
switch (b->Ptype & PTF_ADRMODE) {
case PTA_MINDEXED: case PTA_BYTEPOINT:
if (b->Pindex == p->Pr2) q = NULL; /* Index, lose */
break;
case PTA_REGIS:
if (b->Pr2 == p->Pr2) q = NULL; /* 2nd reg, lose */
break;
}
if (b != q) switch (rchange (b->Pop)) {
case PRC_RCHG_DSAME:
if (b->Ptype == PTA_REGIS && b->Pr2 == p->Pr2 - 1) q = NULL;
case PRC_RSAME: case PRC_RCHG:
if (b->Preg == p->Pr2) q = NULL; /* our register, lose */
break;
case PRC_RSET:
if (b->Preg == p->Pr2) b = NULL; /* our reg safely set */
break;
case PRC_DSAME: case PRC_DCHG:
if (b->Ptype == PTA_REGIS && b->Pr2 == p->Pr2 - 1) q = NULL;
case PRC_DCHG_RSAME:
if (b->Preg == p->Pr2 || b->Preg == p->Pr2 - 1) q = NULL;
break;
case PRC_DSET:
if (b->Ptype == PTA_REGIS && b->Pr2 == p->Pr2 - 1) q = NULL;
case PRC_DSET_RSAME:
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 = P_POP; /* yes, make P_POP */
q->Preg = p->Preg; /* on stack */
if ( ((q->Ptype & PTF_ADRMODE) == PTA_MINDEXED
|| (q->Ptype & PTF_ADRMODE) == PTA_BYTEPOINT)
&& q->Preg == q->Pindex)
q->Poffset--; /* adjust if from stack */
p->Pop = P_NOP; /* drop register P_POP */
p = q; /* use this one instead */
}
}
return adjstack(1, p) + 1;
}
/* ADJSTACK(n, p) - Fix up old ops after stack change shifts.
**
** The section of the buffer from P (exclusive) to the end
** ("previous") has all references to the stack adjusted as if an
** ADJSP 17,N at P had vanished. Thus the effect on the instructions
** is as if the stack pointer had changed by -N from what it used to be.
**
** This is an auxiliary routine used by foldstack() (and its auxiliary
** makepop(); these are the only 2 routines which call it.
**
** The section of the buffer changed is assumed to be safe, i.e. checked
** over by foldstack().
** It returns the net change to the stack offset as a result of
** turning PUSH/POP instructions into ones which don't change the stack
** pointer.
*/
static int
adjstack(n, p)
PCODE *p;
{
PCODE *q;
int c = 0; /* how much we changed it */
/* Loop back until we hit the changed op, and then return. */
for (q = previous; q != p; q = before(q)) {
switch (q->Ptype & PTF_ADRMODE) {
case PTA_MINDEXED:
case PTA_BYTEPOINT: /* If an indexed instruction */
if (q->Pindex == R_SP) /* uses the stack ptr as index */
q->Poffset += n; /* then need to adjust it. */
break;
case PTA_REGIS: /* POP/PUSH P,R turns into MOVE/M R,-n(P) */
if (q->Preg == R_SP) switch (q->Pop) {
case P_POP:
c += adjstack(-1, q);
q->Pop = P_MOVE;
q->Preg = q->Pr2;
q->Ptype = PTA_MINDEXED;
q->Pptr = NULL;
q->Pindex = R_SP;
q->Poffset = n;
c--;
break;
case P_PUSH:
c += adjstack(1, q);
q->Pop = P_MOVEM;
q->Preg = q->Pr2;
q->Ptype = PTA_MINDEXED;
q->Pptr = NULL;
q->Pindex = R_SP;
q->Poffset = n + 1;
c++;
break;
} /* end case PTA_REGIS: switch(q->Pop) */
case PTA_RCONST: /* PUSH P,[0/-1] becomes SETZB/OB 16,-n(P) */
if (q->Pop == P_PUSH) {
c += adjstack(1, q);
q->Pop = (q->Pvalue == 0)? P_SETZ+POF_BOTH : P_SETO+POF_BOTH;
q->Preg = R_SCRREG; /* have to have some reg here */
q->Ptype = PTA_MINDEXED;
q->Pptr = NULL;
q->Pindex = R_SP;
q->Poffset = n + 1;
c++;
break;
}
} /* end switch(q->Ptype) */
}
return c;
}
/* HACKSTACK(label) - Pull beginning-of-routine P_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 P_ADJSP for the
** local variables across the if and return.
** This also works for some cases where there is a JRST instead of a POPJ.
**
** Our argument is the label we are about to emit;
** the return value is the P_ADJSP that must be done
** after the label is (or is not) emitted.
*/
#if 0 /* COMMENT */
The algorithm used here is a little non-obvious and needs some
words of explanation.
Basically, we are looking backwards from our current location
(marked by "label") to see if we can find an ADJSP with a positive
value N. If we find one, we see if it can be flushed entirely or not;
only flushing it partially is no good, since the instruction takes
just as much time regardless of N.
To see whether it can be flushed, we have to know two things:
FIRST, does the stack size change between the ADJSP and our label? If
new cells are added in some way (PUSH is the only way, as an ADJSP
would have been caught) then they must have been flushed off before
reaching our label. We check this by making sure the current stack
offset ("spos") after the ADJSP is the same as that of the stack
offset at our label ("stackoffset"). Only if they are the same is it
safe to move this ADJSP to our label.
SECOND, are any of the N stack cells the ADJSP creates referenced
between there and our label? As we go back, we check each instruction
for a stack reference; but since we don't know beforehand how large N
is, we cannot easily know which references are significant. We only
know two things: (1) Refs with a negative overall stack offset are to
the function arguments and thus are okay, and (2) Refs with a positive
offset higher than "stackoffset" refer to places above N which are
temporary (created by PUSH, removed by POP or ADJSP) and will not
exist by the time we are at our label, and thus are also OK.
To keep track of positive-offset references the same or lower
than "stackoffset", we keep a count "nrefs" of such references, and a
variable "ceiling" which remembers the offset of the largest such
reference seen. It is important to remember that "ceiling" is NOT the
largest positive-offset reference; it is only the largest one that is
at or below "stackoffset".
Thus, when we find the positive ADJSP, first we check "nrefs"; if
there were no qualifying references, we are safe. But otherwise the N
tells us where the N cells started; if our "ceiling" is below this start,
then none of those cells were referenced and we are also safe. If on the
other hand the "ceiling" is above the start of the N cells, then it marks
a reference to one of the N cells and we dare not touch the ADJSP.
#endif
int
hackstack (lab)
SYMBOL *lab;
{
int spos; /* Current stack offset */
int ceiling; /* Largest "affected" stack offset ref */
int nrefs; /* # of such stack references seen */
PCODE *lastadj, *p;
int newtop, adjres;
if (!optobj) return 0;
if (isskip(previous->Pop)) return 0;
spos = stackoffset; /* Set current stack offset */
nrefs = 0; /* No refs yet */
lastadj = NULL; /* haven't found an ADJSP yet */
for (p = previous; p != NULL; p = before (p))
switch (p->Pop & POF_OPCODE) {
case P_JRST: case P_JUMP: /* A jump to someplace */
case P_AOJ: case P_SOJ: /* had better be to our label */
if (p->Pptr == NULL) return 0; /* Make sure it has a label */
if (p->Pptr->Sclass == SC_ILABEL) { /* If it's an internal lab, */
if (p->Pptr != lab) /* and not to our label, */
return 0; /* then must fail, can't track code */
if (spos != stackoffset) /* To our label. Offset gotta match */
return 0; /* Ugh. Probably an error. */
continue; /* OK, can continue. */
}
/* Jump to an external label. This is assumed to be a tail
** recursion optimization (where a PUSHJ + POPJ is replaced by a
** JRST), and so we interpret the jump like a POPJ by dropping
** through.
*/
case P_POPJ: /* Handle POPJ (also fall thru from above) */
spos = 0; /* stack can't exist here */
lastadj = NULL; /* so don't remember ADJSP after */
continue;
case P_PUSH:
/* PUSH of something onto the stack must check two things:
** the address pushed into (top of stack), and
** the address pushed from (address ref).
** We do the top of stack first. This involves two checks of its own;
** First we make sure the object pushed is later removed (by
** seeing whether the current stack offset is smaller than the
** original one farther down in the code).
#if 0
** Second we do an address ref check similar to that done for
** memory operands, except that Poffset is known to be 0.
#endif
**
*/
if (p->Preg == R_SP) { /* If pushing onto stack, check it */
if (spos < stackoffset) /* If putting stuff on stack that */
return 0; /* isn't removed later, give up. */
#if 0
if (nrefs++ == 0
|| spos < ceiling) { /* Update lowest cell reffed */
ceiling = spos;
if (ceiling <= 1) /* If 1st cell or worse, */
return 0; /* quit. */
}
#endif
spos--; /* See comment below */
}
/* PUSH of something onto the stack must bump down the stack offset
** prior to checking address ref, as when the instr is executed
** the address is computed before the stack is bumped. But our
** current stack offset value is that AFTER the push was done.
** So, we decrement the current stack offset and then fall thru into
** the normal address ref checking.
*/
case P_LDB: case P_ADJBP: case P_PUSHJ:
case P_MOVE: case P_MOVN: case P_MOVM: case P_MOVEM:
case P_SETZ: case P_SETO:
case P_AND: case P_IOR: case P_XOR:
case P_ADD: case P_SUB: case P_IMUL: case P_IDIV: case P_UIDIV:
case P_FADR: case P_FSBR: case P_FMPR: case P_FDVR:
case P_DFAD: case P_DFSB: case P_DFMP: case P_DFDV:
case P_SKIP: case P_CAI: case P_CAM:
case P_TRO: case P_TRN: case P_TRC:
case P_TLO: case P_TLZ:
case P_TDO: case P_TDN: case P_TDZ: case P_TDC:
case P_AOS: case P_SOS:
case P_SMOVE:
/* Check out addressing of normal instruction to see if it references
** stack, and if so, where.
** A reference to the args of the current function is OK and can be
** ignored. Otherwise, it is a reference to some local stack cell
** and we check to find whether it is a cell that still exists
** at our label (i.e. ref is same or lower than "stackoffset").
** Then we check to remember the highest such reference. See
** comments at top of page.
*/
#if 1 /* 1st gen, revised to make 3rd gen stuff */
if ( ((p->Ptype & PTF_ADRMODE) != PTA_MINDEXED /* Indexed? */
&& (p->Ptype & PTF_ADRMODE) != PTA_BYTEPOINT)
|| p->Pindex != R_SP) /* Yes, stack idx? */
continue; /* Nope to either, ignore. */
/* Instruction's E uses stack reg as index. Check the reference. */
if (p->Poffset + spos < 0) /* Refers to our fn's args, OK */
continue;
/* Refers to local vars, see if below stackoffset (ok to remember) */
if (p->Poffset + spos > stackoffset)
continue; /* Above, so ignore it */
/* Local var, not a temp, so remember the reference */
if (nrefs++ == 0 || (p->Poffset + spos) > ceiling) {
ceiling = p->Poffset + spos; /* Remember new highest ref */
if (ceiling == stackoffset) /* If ref is to highest possible var */
return 0; /* then even ADJSP 1 would fail. */
}
continue;
#endif
#if 0 /* 2nd gen stuff */
if ( ((p->Ptype & PTF_ADRMODE) == PTA_MINDEXED /* Indexed? */
|| (p->Ptype & PTF_ADRMODE) == PTA_BYTEPOINT)
&& p->Pindex == R_SP /* Yes, stack idx? */
&& (p->Poffset + spos) >= 0 /* Refers to local vars? */
&& (nrefs++ == 0 /* If ref is first */
|| (p->Poffset + spos) < ceiling)) { /* or lowest so far */
ceiling = p->Poffset + spos; /* Remember new lowest ref */
if (ceiling <= 1) /* If first var (== 1) or worse, */
return 0; /* give up. */
}
continue;
#endif
case P_ADJSP:
if (p->Pvalue < 0) {
if (lastadj == NULL && spos == 0)
lastadj = p; /* If we win, can stop fixup here */
spos -= p->Pvalue; /* change stack offset */
continue;
}
/*
** If we got here, we've finally found our positive P_ADJSP.
**
** First check it against the ceiling we've calculated;
** if any reference was seen to something that the ADJSP grabbed,
** then we cannot flush the ADJSP. See comments at top of page.
**
** Likewise, if any reference was made to something put on the stack
** with a PUSH then it must have been taken off later with a POP
** (not handled -- always fails) or an ADJSP with negative operand.
** This means the stack offset at this point (spos) must match that
** which was originally in effect (stackoffset).
**
** Then we run through the ops between here and the final P_ADJSP
** (the one before the P_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 (stackoffset != spos)
return 0; /* Make sure stack offsets come out even */
if (nrefs
&& ceiling > (spos - p->Pvalue)) /* Ref to any of N cells? */
return 0; /* Yes, fail */
adjres = p->Pvalue; /* Won! Remember how much to move */
spos -= p->Pvalue; /* Adjust current stack offset */
newtop = spos; /* Remember that as new top of stk */
p->Pop = P_NOP; /* drop the P_ADJSP */
/* Now move forward through stack, adjusting all stack refs
** Note loop stops when hit lastadj (which may be NULL)
*/
while ((p = after (p)) != lastadj) {
if (( (p->Ptype & PTF_ADRMODE) == PTA_MINDEXED
|| (p->Ptype & PTF_ADRMODE) == PTA_BYTEPOINT)
&& p->Pindex == R_SP
&& (p->Poffset + spos) <= newtop) /* If refs stk below gap */
p->Poffset += adjres; /* adjust it */
switch (p->Pop) {
case P_PUSH:
spos++;
break;
case P_ADJSP:
spos += p->Pvalue; /* Account for ADJSP change to stack */
if (spos < newtop) /* Ensure it didn't take off too much*/
error(EINT,"hackstack() fouled up");
break;
}
}
if (p != NULL) { /* still have to deal with the ADJSP */
p->Pvalue += adjres; /* take out the variable part */
if (p->Pvalue == 0)
p->Pop = P_NOP; /* or drop the whole thing */
}
return adjres; /* this is how much we adjusted */
default: return 0; /* some strange opcode */
}
return 0; /* broke out means no P_ADJSP in block */
}
/* KILLSTACK() - 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 */
void
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 & POF_OPCODE) {
case P_MOVEM: case P_AOS: case P_SOS: case P_DMOVEM:
case P_SETCM: case P_MOVN: case P_SETO: case P_SETZ:
case P_FADR: case P_FSBR: case P_FMPR: case P_FDVR:
case P_ADD: case P_IMUL: case P_SUB: case P_AND:
case P_IOR: case P_XOR: case P_IBP: case P_ADJBP:
case P_DPB: case P_LDB: case P_IDPB: case P_ILDB:
case P_MOVE: case P_LSH: case P_FIX: case P_FLTR:
case P_HRRZ: case P_HRRM: case P_HLRZ: case P_HRLM:
case P_DFAD: case P_DFSB: case P_DFMP: case P_DFDV:
case P_TDO: case P_TDZ: case P_TDN: case P_TDC:
case P_TRO: case P_TRZ: case P_TRN: case P_TRC:
case P_TLO: case P_TLZ: case P_TLN: case P_TLC:
case P_CAI: case P_CAM: case P_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 & PTF_ADRMODE) {
case PTA_BYTEPOINT:
case PTA_MINDEXED:
if (p->Pindex == R_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 & PTF_IND)) switch (p->Pop&POF_OPCODE) {
case P_MOVEM:
case P_HRRM:
case P_HRLM:
case P_DMOVEM:
p->Pop = P_NOP; /* drop losing P_MOVEM */
fixprev(); /* make sure fully gone */
p = before(p); /* skip back to avoid remembering */
continue; /* this as a stack ref */
case P_SETO:
case P_SETZ:
p->Ptype = PTA_ONEREG; /* ignore all memory in op */
default:
if ((op = (p->Pop &~ POF_BOTH)) == p->Pop) break; /* nothing to do */
p->Pop = P_MOVE; /* make P_MOVE for cse check */
s = foldcse (r = p->Preg, p); /* look for it in another reg */
if (p->Pop != P_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 = PTA_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 P_SKIPA+P_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 P_PUSH:
/*
** We found a P_PUSH, and since we can't handle P_ADJSP this must
** have been a P_MOVEM to the then top of the stack.
**
** What we do is turn it into an P_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 P_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 = P_ADJSP; /* no, turn into P_ADJSP */
p->Ptype = PTA_RCONST; /* of constant amount */
p->Pvalue = 1; /* one word no longer pushed */
} /* then fall into unknown */
default:
return; /* unknown op, give up on it */
}
}
/* ONEINSTR(p) - 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 & POF_OPCODE) {
case P_SUBBP: /* These always expand out */
case P_DFIX:
return 0;
/* These instructions may or may not expand out.
** None of them have an "immediate" form, so PTV_IINDEXED can never happen.
*/
case P_DMOVE: case P_DMOVN: case P_DMOVEM:
return tgmachuse.dmovx; /* TRUE if machine has DMOVx */
case P_ADJBP:
return tgmachuse.adjbp; /* TRUE if machine has ADJBP */
case P_DFAD: case P_DFSB: case P_DFMP: case P_DFDV:
return tgmachuse.dfl_h; /* TRUE if machine has hardware dbls */
case P_MOVE:
return 1; /* ok even for PTV_IINDEXED */
case P_TRN: case P_TRO: case P_TRC: case P_TRZ:
case P_TLN: case P_TLO: case P_TLC: case P_TLZ:
case P_LSH: case P_ASH: case P_CAI:
return (p->Ptype != PTA_MINDEXED); /* PTV_IINDEXED for implicit immed op */
default: /* OPI R,addr expands to */
return (p->Ptype != PTV_IINDEXED); /* P_XMOVEI 16,addr / OP r,16 */
}
}
/* UNSETZ(p) - Turn SETZ R, back into MOVEI R,0.
**
** Used by optimizations that want to only deal with P_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 P_MOVN:
case P_SUB:
if ((p->Ptype &~ PTF_SKIPPED) != PTV_IMMED) return 0;
p->Pvalue = - p->Pvalue;
p->Pop = (p->Pop == P_MOVN? P_MOVE : P_ADD);
case P_MOVE:
case P_ADD:
return 1;
case P_SETZ:
case P_SETO:
if ((p->Ptype &~ PTF_SKIPPED) != PTA_ONEREG) return 0;
p->Ptype ^= (PTA_ONEREG ^ PTV_IMMED);
p->Pvalue = (p->Pop == P_SETZ? 0 : -1);
p->Pop = P_MOVE;
return 1;
default:
return 0;
}
}