Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - kccdist/kcc/ccjskp.c
There are 8 other files named ccjskp.c in the archive. Click here to see a list.
/*	CCJSKP.C - Peephole Optimizer - skips and jumps
**
**	All changes after version 67 (8/8/85), unless otherwise specified, are
**	Copyright 1985, 1986 by Ken Harrenstien, SRI International.
*/
/*
** ccjskp - KCC peephole optimization for skips and jumps
** module created by David Eppstein / Stanford University / 12 Jun 85
*/

#include "cc.h"
#include "ccgen.h"

/* Imported functions */
extern PCODE *before(), *after();

/* Exported functions */
void foldskip();	/* CCCODE */
void inskip();		/* CCCODE, CCOPT */
void foldjump();	/* CCCODE */
int safejump();		/* CCCODE, CCGEN1 */
int dropsout();		/* CCCREG, CCCSE, CCOPT */
void unskip();		/* CCCREG */
int unjump();		/* CCGEN2 */
void optlab();		/* CCCODE */
int foldtrna();		/* CCOUT */

/* Internal functions */
static void dropjump(), crossjump();
static int invskip();

typedef SYMBOL *label;
/* SAFEJUMP(p) - See if currently emitted code will be dead.
**
** There are three cases (yes the third one actually happens):
**  (1) The last op was an unskipped P_JRST, i.e. an unconditional jump
**  (2) The last op was an unskipped P_POPJ, i.e. an unconditional return
**  (3) The last op was an P_IFIW, i.e. we're in a switch jump table
*/

int
safejump (p)
PCODE *p;
{
    if (p != NULL && optobj) switch (p->Pop) {
	case P_JRST:		/* jump, return, or jump table? */
	case P_POPJ:
	case P_IFIW:
	return !prevskips (p);		/* yes, make sure not skippable over */
    }
    return 0;				/* can't see or not jump, fail */
}
/* DROPSOUT(p) - See if operation can possibly affect following instructions
**
** It can't if it's a JRST, it's a POPJ, or its POF_OPSKIP field is POS_SKPA
** and it skips to an operation that can't affect the stream.
*/

int
dropsout (p)
PCODE *p;
{
    if (p == NULL) return 0;
    if (p->Pop == P_JRST || p->Pop == P_POPJ) return 1;
    if ((p->Pop & POF_OPSKIP) != POS_SKPA) return 0;
    if ((p = after (p)) == NULL || (p = after (p)) == NULL) return 0;
    return dropsout (p);
}
/* DROPJUMP(p) - Remove a jump instruction
**
** Actually this removes any instruction, but it is necessary to use if
** the instruction might be a jump so the label refcount can be maintained.
*/

static void
dropjump (p)
PCODE *p;
{
    if ((p->Ptype & PTF_ADRMODE) == PTA_MINDEXED) reflabel (p->Pptr, -1);
    p->Pop = P_NOP;			/* drop the instruction */
    fixprev();				/* fix up previous */
}
/* INVSKIP(p) - Invert an imbedded skip
**
** Takes a skip instruction in the peephole buffer and makes the code there
** skip on the opposite condition.  No new code is emmited (unlike makeskip()).
** Checks to make sure skip can't be itself skipped over.
*/

static int
invskip (p)
PCODE *p;
{
    int r;
    PCODE *q, *b;

    if (p == NULL) return 0;		/* nothing to invert */

    if (!isskip(p->Pop)) return 0;

    if (p->Pop == P_CAI+POF_ISSKIP+POS_SKPLE && p->Ptype == PTA_RCONST+PTF_SKIPPED &&
	(q = before (p)) != NULL && q->Ptype == PTA_RCONST && q->Preg == p->Preg &&
	q->Pop == P_CAI+POF_ISSKIP+POS_SKPL && q->Pvalue == p->Pvalue - 1) {

	q->Pop = P_CAI+POF_ISSKIP+POS_SKPE;	/* invert P_CAIL R,n / P_CAILE R,n+1 */
	p->Pop = P_CAI+POF_ISSKIP+POS_SKPN;	/* to     P_CAIE R,n / P_CAIN R,n+1 */
	return 1;
    }

    r = revop(p->Pop);
    if (!(r & POF_OPSKIP)) {		/* was POS_SKPA, now no skip at all */
	r &=~ POF_ISSKIP;			/* so don't think of it as a skip */
	if (r == P_TRN) {			/* was P_TRNA, don't need to check */
	    p->Pop = P_NOP;		/* if skipped, just drop entirely */
	    fixprev();			/* be careful, might have been prev */
	    return 1;			/* that's all for now gone P_TRNA */
	}
	if ((q = before (p)) != NULL && (q->Pop & POF_OPSKIP) == POS_SKPA) {
	    if (!invskip (before (q))) return 0; /* two unflippable POS_SKPAs */
	    swappseudo (p, q);		/* flipped before, now them */
	    return 1;			/* win win */
	}
    }

    if (!prevskips (p)) {
	p->Pop = r;			/* single skip, invert it */
	return 1;			/* return success */
    }

    /* look for three-word flip from double comparison */
    if ((p->Ptype & PTF_ADRMODE) != PTA_REGIS || !(p->Pop & POSF_CMPSKIP) ||
	(p->Pop & POF_OPCODE) != P_CAM || (q = before (p)) == NULL ||
	(b = before (q)) == NULL || b->Pop != (p->Pop ^ POSF_EQSKIP) ||
	(b->Ptype & PTF_ADRMODE) != PTA_REGIS || b->Preg != p->Preg ||
	b->Pr2 != p->Pr2) return 0;

    /* have a doubleword compare, invert the whole thing */
    q->Pop = revop (q->Pop);
    p->Pop = revop (b->Pop);
    b->Pop = r;
    return 1;
}
/* FOLDSKIP(p) - Turn P_CAIx R,0 into something better
**
** The safechange argument is zero if it might be part of a switch
** (in which case we can't safely change the value in the reg).
*/

void
foldskip (p, safechange)
PCODE *p;
{
    PCODE *q;

    if (p == NULL || !optobj) return;

    /*
    ** fold:  P_MOVE S,x
    ** 	      P_ADD S,y
    ** 	      P_CAMN R,S
    **
    ** into:  P_SUB R,x
    ** 	      P_SUB R,y
    ** 	      P_CAIN R,0
    **
    ** (only for P_CAMN and P_CAME, to avoid wrap problems).
    */

    if ((p->Pop == P_CAM+POF_ISSKIP+POS_SKPE
      || p->Pop == P_CAM+POF_ISSKIP+POS_SKPN) &&
	p->Ptype == PTA_REGIS)
    for (q = before (p); q != NULL; q = before (q)) {
	if (prevskips (q) || q->Preg != p->Pr2) break;
	switch (q->Pop) {
	case P_ADD: case P_SUB:
	    q->Preg = p->Preg;		/* swap adds across to other reg */
	    q->Pop ^= (P_ADD ^ P_SUB);	/* making them subtracts instead */
	    continue;

	case P_MOVE: case P_MOVN:
	    q->Preg = p->Preg;		/* same for P_MOVE and P_MOVN */
	    q->Pop = (q->Pop == P_MOVE? P_SUB : P_ADD); /* turned into P_SUB and P_ADD */
	    p->Pop ^= (P_CAM ^ P_CAI);	/* make immediate comparison */
	    p->Ptype = PTA_RCONST;		/* against number */
	    p->Pvalue = 0;		/* number is zero */
	}
	break;				/* escape loop if not explicit cont */
    }

    if ((p->Pop & POF_OPCODE) != P_CAI) return; /* must be P_CAIx */
    q = before(p);			/* look before it */

    if (q != NULL && q->Ptype == PTA_REGIS /* !prevskips */
     && q->Preg == p->Preg && q->Pop == P_MOVE && safechange) {

	/*
	** fold:  P_MOVE  R,S
	**        P_CAIx  R,c
	**
	** into:  P_CAIx  S,c
	*/

	p->Preg = q->Pr2;		/* flatten tested register */
	q->Pop = P_NOP;			/* flush useless move */
	q = before(q);			/* try for more optimizations */
    }

    if (q->Preg == p->Preg && q->Ptype == PTV_IMMED && safechange)
	switch(q->Pop) {
	    case P_SUB:
		if (!unsetz (q)) break;
	    case P_ADD:

		/*
		** fold:  P_ADDI  R,n
		**        P_CAIx  R,m
		**
		** into:  P_CAIx  R,m-n
		*/

		p->Pvalue -= q->Pvalue;
		q->Pop = P_NOP;			/* flush folded addition */
		q = before(q);			/* look for more before it */
	}

    /*
    ** fold:  P_CAIGE R,1
    ** into:  P_CAIG  R,0
    */

    if ((p->Ptype & PTF_ADRMODE) != PTA_RCONST) return;	/* rest of opts need numbers */

    switch (p->Pop & POF_OPSKIP) {
    case POS_SKPG:    case POS_SKPLE:		/* skip that can be adjusted up? */
	if (p->Pvalue == -1 || p->Pvalue == -2) { /* worthwhile to do it? */
	    p->Pvalue++;		/* yes, increment value */
	    p->Pop ^= POSF_EQSKIP;		/* and adjust skip to match */
	}
	break;

    case POS_SKPL:    case POS_SKPGE:		/* skip that can be adjusted down? */
	if (p->Pvalue == 1 || p->Pvalue == 2) {	/* worthwhile to do it? */
	    p->Pvalue--;		/* yes, decrement value */
	    p->Pop ^= POSF_EQSKIP;		/* and adjust skip to match */
	}
	break;
    }

    /*
    ** Fold remaining comparisons against one into P_AOSx which will
    ** later become P_AOJx.  We make P_AOS R,R rather than P_AOS R so we
    ** don't confuse the rest of code generation too badly.
    ** 
    ** All optimizations after this one can safely assume that the
    ** value being compared against is zero.
    */

    switch (p->Pvalue) {
    case 1:
	if (!safechange) return;	/* not for switch you don't */
	p->Pop ^= (P_CAI ^ P_SOS);		/* make P_SOSx */
	p->Ptype ^= (PTA_RCONST ^ PTA_REGIS);	/* from and to register */
	p->Pr2 = p->Preg;		/* same register */
	return;				/* that's all */

    case -1:
	if (!safechange) return;	/* not for switch you don't */
	p->Pop ^= (P_CAI ^ P_AOS);		/* make P_AOSx */
	p->Ptype ^= (PTA_RCONST ^ PTA_REGIS);	/* from and to register */
	p->Pr2 = p->Preg;		/* same register */
	return;				/* that's all */

    case 0: break;			/* zero is ok to continue with */
    default: return;			/* anything else we can't handle */
    }

    /*
    ** The remaining optimizations fold a test against zero with
    ** the previous instruction.
    */

    if (q->Preg != p->Preg) {
	if ((q->Pop == P_IDIV || q->Pop == P_UIDIV)
		&& q->Preg + 1 == p->Preg
		&& safechange
		&& q->Ptype == PTV_IMMED /* !prvsk */
		&& ((q->Pvalue-1)&q->Pvalue) == 0) {

	    /*
	    ** fold:  P_IDIVI R-1,2^n
	    **        P_CAIx  R,0
	    **
	    ** into:  P_TRNx  R,2^n-1
	    */

	    switch (p->Pop & POF_OPSKIP) {
	    case POS_SKPE:
	    case POS_SKPN:
		p->Pop ^= (P_CAI ^ P_TRN);
		p->Preg = q->Preg;
		p->Ptype = PTA_RCONST;
		p->Pvalue = q->Pvalue - 1;
		q->Pop = P_NOP;
		q = before(q);		/* now look at before the P_IDIVI */
		if (q->Ptype == PTA_REGIS /* !prevskips */ && q->Pop == P_MOVE &&
		    q->Preg == p->Preg) {
		    p->Preg = q->Pr2;
		    q->Pop = P_NOP;	/* flatten failed changereg() */
		}
	    }
	}
	return;
    }

    if (prevskips (q)) return;
    switch (q->Pop) {
    case P_AND:
	if (!safechange) return;
	switch (p->Pop & POF_OPSKIP) {
	case POS_SKPE:
	case POS_SKPN:

	    /*
	    ** fold:  P_AND  R,mask
	    **	      P_CAIE R,0
	    **
	    ** into:  P_TDNE R,mask
	    */

	    if (q->Ptype == PTV_IMMED) {
		q->Ptype = PTA_RCONST;
		q->Pop = p->Pop ^ (P_CAI ^ P_TRN);
	    } else q->Pop = p->Pop ^ (P_CAI ^ P_TDN);
	    break;

	default: return;
	}
	break;

    case P_MOVE:
	/*
	** fold:  P_MOVE  R,addr
	**        P_CAIx  R,0
	**
	** into:  P_SKIPx R,addr
	*/
	q->Pop = p->Pop ^ (P_CAI ^ P_SKIP);
	break;

    case P_ADD:
	if (q->Ptype != PTV_IMMED || !safechange) return;
	if (p->Pop != P_CAI+POF_ISSKIP+POS_SKPE && p->Pop != P_CAI+POF_ISSKIP+POS_SKPN) return;
	/* Now that we know we'll do the optimization, safe to clobber instr */
	q->Pvalue = - q->Pvalue;

    case P_SUB:
	if (!safechange) return;

	/*
	** fold:  P_SUB R,addr
	**  	  P_CAIN R,0
	**
	** into:  P_CAMN R,addr
	**
	** (only for P_CAMN or P_CAME, not other comparisons)
	*/

	if (p->Pop != P_CAI+POF_ISSKIP+POS_SKPE && p->Pop != P_CAI+POF_ISSKIP+POS_SKPN) return;
	q->Pop = p->Pop;		/* make comparison */
	if (q->Ptype & PTF_IMM) q->Ptype &=~ PTF_IMM; /* immediate uses P_CAI form */
	else q->Pop ^= (P_CAM ^ P_CAI);	/* memory uses P_CAM form */
	break;

    case P_AOS:	case P_SOS:

	/*
	** fold:  P_AOS   R,addr
	**        P_CAIx  R,0
	**
	** into:  P_AOSx  R,addr
	*/

	q->Pop |= (p->Pop &~ POF_OPCODE);
	break;

    default: return;
    }

    /*
    ** All the defaults in the previous switch should be return statements,
    ** so if we made it through we must have made a fold.  We should therefore
    ** flush the P_CAIx so we don't have two skips in a row.
    */

    p->Pop = P_NOP;			/* flush it */
    fixprev();				/* make sure previous valid */
}
/* JUMPTOSKIP(p) - Turn a jump into an immediate compare against zero.
** 
** Assumes there is one instruction between the jump and its
** label; if only zero the resulting skip should be reversed.
** Doesn't update PTF_SKIPPED in following opcodes.
*/

static void
jumptoskip (p)
PCODE *p;
{
    if ((p->Ptype &~ PTF_SKIPPED) == PTA_MINDEXED) switch (p->Pop & POF_OPCODE) {
    case P_JRST:
	if (invskip (before (p))) {	/* can flip previous skips */
	    dropjump (p);		/* so having done it just drop jump */
	    return;
	}
	reflabel (p->Pptr, -1);		/* fix up reference counts */
	p->Pop = P_TRN+POF_ISSKIP+POS_SKPA;	/* now turn it into a P_TRNA */
	p->Ptype ^= (PTA_MINDEXED ^ PTA_ONEREG); /* no address */
	p->Preg = 0;			/* no register either */
	return;

    case P_JUMP:
	reflabel (p->Pptr, -1);		/* fix up reference counts */
	p->Pop ^= (P_CAI ^ P_JUMP ^ POF_ISSKIP); /* make P_CAIx */
	p->Ptype ^= (PTA_MINDEXED ^ PTA_RCONST); /* immediate compare */
	p->Pvalue = 0;			/* against zero */
	return;

    case P_AOJ:
	reflabel (p->Pptr, -1);		/* fix up reference counts */
	p->Pop ^= (P_AOS ^ P_AOJ ^ POF_ISSKIP);	/* make P_AOSx */
	p->Ptype ^= (PTA_MINDEXED ^ PTA_REGIS);	/* reg to reg */
	p->Pr2 = p->Preg;		/* both same reg */
	return;

    case P_SOJ:
	reflabel (p->Pptr, -1);		/* fix up reference counts */
	p->Pop ^= (P_SOS ^ P_SOJ ^ POF_ISSKIP);	/* make P_SOSx */
	p->Ptype ^= (PTA_MINDEXED ^ PTA_REGIS);	/* reg to reg */
	p->Pr2 = p->Preg;		/* both same reg */
	return;
    }
}
/* CROSSJUMP(label) - Pull P_JUMPx across skips
**
** Sometimes it can improve code to move a P_JUMPx, P_AOJx, or P_SOJx from one
** side of a set of cascaded skips to the other.  We have to be careful
** though in rearranging this sort of thing that we don't get illegal
** memory references or indirection loops.
**	KLH: One problem with this code is that the two code sequences
** are not equivalent.  That is, the registers may end up with different
** values, since the skips may have modified the regs.  The initial sequence
** would not do this modification, whereas the new sequence does.
*/


static void
crossjump (lab)
label lab;
{
#if 0
    int regok[NREGS], i;
#endif
    PCODE *q, *p;
    int op;

    /*
    ** fold:  JUMPx  R,L1		; JUMPx, AOJx, or SOJx
    **	      (any number of skips)	; for JUMP, cannot change R or any mem
    **					;         also cannot use R in address
    **					; for A/SOJ, ditto + can't use R as reg
    **	      JRST   L2
    **	      JRST   L1
    **
    ** into:  (the same skips)
    **	      JUMPy  R,L2
    **	      JRST   L1
    */
    if (previous->Pop != P_JRST) return;	/* must be a jump here */

#if 1
    for (q = before(previous); ; q = before(q)) {
	if (q == NULL) return;
	if (!isskip(q->Pop))		/* Non-skip instr halts loop */
	    break;
	if ((popflg[q->Pop&POF_OPCODE]&PF_MEMCHG)	/* Any mem chg instr */
	  || (q->Ptype & PTF_IND))			/* or indirect ref */
		return;					/* halts the search. */
    }
    switch (op = (q->Pop & POF_OPCODE)) {
	case P_AOJ:
	case P_SOJ:
	case P_JUMP:
	    if (q->Pptr != lab || prevskips(q))
		return;			/* Wrong label or op is skipped */

	    /* Re-scan the skips to ensure they do not use R unsafely.
	    ** For JUMP, just ensure the skip does not modify R, and does not
	    **	use it in an address.
	    ** For AOJ/SOJ, must ensure R is not referenced at all.
	    */
	    for (p = before(previous); p != q; p = before(p)) {
		if (rinaddr(p, q->Preg) ||	/* R cannot be in addr */
		  ((op == P_JUMP)
		  ? rmodreg(p, q->Preg)		/* Cannot modify R */
		  : rinreg(p, q->Preg)))	/* Cannot reference R */
		    return;
	    }
	    /* Won, OK to move and invert the jump. */
	    previous->Preg = q->Preg;		/* else copy reg to jump on */
	    previous->Pop = revop (q->Pop);	/* invert jump cond */
	    dropjump(q);			/* drop original P_JUMPx */
    }
#else	/* Old stuff */
    for (i = 0; i < NREGS; i++) regok[i] = 1; /* reg not yet seen */
    for (q = before (previous); q != NULL; q = before (q))
    switch (q->Pop & POF_OPCODE) {

    case P_AOJ: case P_SOJ:
	if (regok[q->Preg] < 0) return;	/* not safe to commute */
    case P_JUMP:
	if (q->Pptr != lab || !regok[q->Preg] || prevskips (q)) return;
	previous->Preg = q->Preg;	/* else copy register to jump on */
	previous->Pop = revop (q->Pop);	/* invert jump cond */
	dropjump(q);			/* drop original P_JUMPx */
	return;				/* that's all */

    default:
	if (!isskip(q->Pop))		/* Any non-skip instruction */
	    return;			/* halts search immediately. */
	regok[q->Preg] = 0;

    case P_TDN: case P_TLN: case P_TRN:	/* Skips that don't change anything */
    case P_CAI: case P_CAM:
	if (q->Ptype & PTF_IND) return;	/* careful about @ loops */
	switch (q->Ptype & PTF_ADRMODE) {	/* look at type of op */
	case PTA_FCONST:
	case PTA_DCONST:
	case PTA_DCONST1:
	case PTA_DCONST2:
	case PTA_RCONST:
	case PTA_ONEREG:
	case PTA_PCONST:
	    break;		 /* no 2nd reg ops */
	case PTA_REGIS:
	    regok[q->Pr2] = 0;
	    break;
	case PTA_MINDEXED:
	case PTA_BYTEPOINT:
	    regok[q->Pindex] = 0;
	    break;
	default:
	    error(EINT,"illegal Ptype seen in crossjump()");
	}
	if (regok[q->Preg]) regok[q->Preg] = -1; /* not safe for P_AOJx */
	continue;
    }
#endif
}
/* FOLDJUMP(label) - Common optimizations for labels and jumps
**
** Called either when about to emit a jump to the given label, or when about
** to emit the label itself.  There are some things we can do in optlab()
** that we can't do here, because (for instance) we don't want to turn a
** jump two before another to the same place into a P_TRNA to the jump.
*/

void
foldjump (lab)
label lab;
{
    PCODE *q, *b;
    int r;

    if (!optobj || previous == NULL || !oneinstr(previous) ||
	(q = before(previous)) == NULL || (b = before(q)) == NULL) return;

    if (q->Pop == P_JRST && q->Pptr == lab && !(q->Ptype & PTF_IND) && invskip(b)) {
	/*
	** fold:  skip
	**        P_JRST n
	**        op
	**        P_JRST n
	**
	** into:  reverse skip
	**        op
	**        P_JRST n
	*/

	dropjump(q);			/* already flipped skip, drop jump */
	setskip (previous);		/* op is now skipped over */
	if (previous->Pop == P_MOVN && b->Preg == previous->Preg && !prevskips(b)
	    && (b->Pop == P_SKIP+POF_ISSKIP+POS_SKPG || b->Pop == P_SKIP+POF_ISSKIP+POS_SKPGE) &&
	    (sameaddr (b, previous, 0) || (previous->Ptype == PTF_SKIPPED+PTA_REGIS &&
					   previous->Preg == previous->Pr2))) {

	    /*
	    ** fold:  P_SKIPGE 1,x
	    ** 	      P_MOVN 1,1
	    **
	    ** into:  P_MOVM 1,x
	    */

	    b->Pop = P_MOVM;		/* make P_MOVM */
	    previous->Pop = P_NOP;	/* drop P_MOVN */
	    fixprev();			/* fix up to point to last op (P_MOVM) */
	}
    } else if (isskip (q->Pop) && b->Pop == P_JRST && b->Pptr == lab &&
	       invskip (before (b))) {
	/*
	** fold:  skip1
	**        P_JRST n
	**        skip2
	**        op
	**        P_JRST n
	**
	** into:  reverse skip1
	**        reverse skip2
	**        P_JRST n
	**        op
	**        P_JRST n
	*/

	q->Pop = revop(q->Pop);		/* reverse the second skip */
	setskip (q);			/* skip now will be skipped over */
	swappseudo(b, q);		/* switch the skip and the jump */
	clrskip (previous);		/* op no longer skipped over */
    }

    crossjump (lab);			/* pull P_JUMPx across skips */
}
/* NEWSKIP(p) - Turn non-skip op into a skip
**
** I.e. P_MOVE becomes P_SKIPA.  Works even if op is skipped over
** (caller must check for that case).  Again, afters are not set PTF_SKIPPED.
*/

static int
newskip (p)
PCODE *p;
{
    PCODE *q;

    if (p != NULL) switch (p->Pop) {
    case P_SUB:
	if ((p->Ptype &~ PTF_SKIPPED) != PTV_IMMED || p->Pvalue != 1) break;
	p->Ptype ^= (PTV_IMMED ^ PTA_REGIS);
	p->Pr2 = p->Preg;
    case P_SOS:
	p->Pop = P_SOS+POF_ISSKIP+POS_SKPA;
	return 1;

    case P_ADD:
	if ((p->Ptype &~ PTF_SKIPPED) != PTV_IMMED || p->Pvalue != 1) break;
	p->Ptype ^= (PTV_IMMED ^ PTA_REGIS);
	p->Pr2 = p->Preg;
    case P_AOS:
	p->Pop = P_AOS+POF_ISSKIP+POS_SKPA;
	return 1;

    case P_IOR:
	if (p->Ptype & PTF_IMM) {
	    p->Pop = P_TRO+POF_ISSKIP+POS_SKPA;
	    p->Ptype &=~ PTF_IMM;
	} else p->Pop = P_TDO+POF_ISSKIP+POS_SKPA;
	return 1;

    case P_XOR:
	if (p->Ptype & PTF_IMM) {
	    p->Pop = P_TRC+POF_ISSKIP+POS_SKPA;
	    p->Ptype &=~ PTF_IMM;
	} else p->Pop = P_TDC+POF_ISSKIP+POS_SKPA;
	return 1;

    case P_SETZ:
	if ((p->Ptype &~ PTF_SKIPPED) != PTA_ONEREG) break;
	p->Pop = P_TDZ+POF_ISSKIP+POS_SKPA;
	p->Ptype ^= (PTA_ONEREG ^ PTA_REGIS);
	p->Pr2 = p->Preg;
	return 1;

    case P_SETO:   case P_MOVN:   case P_MOVE:
	if (!unsetz (p) || p->Ptype == PTV_IINDEXED) break; /* no XP_SKIPAI */
	p->Pop = P_SKIP+POF_ISSKIP+POS_SKPA;
	if ((p->Ptype &~ PTF_SKIPPED) == PTA_REGIS && (q = before (p)) != NULL &&
	    q->Preg == p->Pr2 && !prevskips (q)) switch (q->Pop & POF_OPCODE) {
	case P_SKIP: case P_AOS: case P_SOS:

	    /*
	    ** fold:  P_SKIPx S,x
	    ** 	      P_SKIPA R,S
	    **
	    ** into:  P_SKIPx R,x
	    */

	    q->Pop = revop (q->Pop);	/* invert skip */
	    q->Preg = p->Preg;		/* set reg */
	    p->Pop = P_NOP;		/* drop extra skip */
	    fixprev();			/* fix up previous */
	}
	    
	return 1;
    }
    return 0;
}
/* UNSKIP(p) - Turn skip back into non-skip
** Undoes the actions of newskip(), and turns off conditional skips.
**
** Safe to be called on non-skip ops like P_JRST and P_POPJ.
*/

void
unskip (p)
PCODE *p;
{
    PCODE *q;

    if (!isskip (p->Pop)) return;	/* be safe for P_JUMPx */
    switch (p->Pop &= POF_OPCODE) {
    case P_SOS:
	if ((p->Ptype &~ PTF_SKIPPED) != PTA_REGIS || p->Pr2 != p->Preg) return;
	p->Pop = P_SUB;			/* P_SOSA R,R */
	p->Ptype ^= (PTA_REGIS ^ PTV_IMMED);	/* becomes P_SUBI R,1 */
	p->Pvalue = 1;
	return;

    case P_AOS:
	if ((p->Ptype &~ PTF_SKIPPED) != PTA_REGIS || p->Pr2 != p->Preg) return;
	p->Pop = P_ADD;			/* P_AOSA R,R */
	p->Ptype ^= (PTA_REGIS ^ PTV_IMMED);	/* becomes P_ADDI R,1 */
	p->Pvalue = 1;
	return;

    case P_TRO:
	p->Ptype |= PTF_IMM;
    case P_TDO:
	p->Pop = P_IOR;
	return;

    case P_TRC:
	p->Ptype |= PTF_IMM;
    case P_TDC:
	p->Pop = P_XOR;
	return;

    case P_TDZ:
	if ((p->Ptype &~ PTF_SKIPPED) != PTA_REGIS || p->Pr2 != p->Preg) return;
	p->Pop = P_SETZ;			/* P_TDZA R,R */
	p->Ptype ^= (PTA_REGIS ^ PTA_ONEREG);	/* becomes P_SETZ R, */
	return;

    case P_SKIP:
	p->Pop = P_MOVE;
	if ((p->Ptype &~ PTF_SKIPPED) != PTV_IMMED || p->Pvalue != -1) return;
	p->Pop = P_SETO;
	p->Ptype ^= (PTV_IMMED ^ PTA_ONEREG);
	return;

    case P_TRN: case P_TDN: case P_TLN: case P_CAI: case P_CAM: /* no-ops */
	p->Pop = P_NOP;			/* flush it all the way */
	fixprev();			/* fix up previous */
	unskip (before (p));		/* make sure no spurious skippage */
    }
    /* default is safe, just op with no skip part */
}
/* OPTLAB(label) - Optimize code in front of label
**
** If we know the label is coming after the next instruction, we can
** make several changes, mostly turning jumps into skips, that improve
** code and may make it possible not to emit the label.
*/

void
optlab (lab)
label lab;
{
    int op;
    PCODE *p, *q, *b, *bb;

    if (!optobj) return;

    /* fold useless jumps and preceeding skips into nothing */
    if ((previous->Pop == P_JRST || (previous->Pop & POF_OPCODE) == P_JUMP) &&
	previous->Pptr == lab && !(previous->Ptype & PTF_IND)) {

	dropjump (previous);		/* drop the jump or skip */
	unskip (previous);		/* disable the skip before it */
    }

    p = before(previous);
    if (p != NULL && oneinstr(previous) && p->Pop == P_CAI+POF_ISSKIP+POS_SKPE &&
	p->Ptype == PTA_RCONST && (q = before(p)) != NULL && q->Pop == P_JRST &&
	q->Pptr == lab && (b = before(q)) != NULL && !prevskips (b))
    switch(b->Pop) {
	/*
	** fold:  P_CAIN R,x
	**        P_JRST lab
	**        P_CAIE R,x+1
	**        instr
	**     lab:
	**
	** into:  P_CAIL R,x
	**        P_CAILE R,x+1
	**        instr
	**     lab:
	*/

    case P_AOS+POF_ISSKIP+POS_SKPN:
	if (p->Pvalue == 1) {
	    b->Pop = P_AOS+POF_ISSKIP+POS_SKPL;
	    dropjump(q);
	    p->Pop = P_CAI+POF_ISSKIP+POS_SKPLE;
	    setskip (p);
	}
	break;

    case P_SKIP+POF_ISSKIP+POS_SKPN:
	if (p->Pvalue == 1) {
	    b->Pop = P_SKIP+POF_ISSKIP+POS_SKPL;
	    dropjump(q);
	    p->Pop = P_CAI+POF_ISSKIP+POS_SKPLE;
	    setskip (p);
	}
	break;

    case P_SOS+POF_ISSKIP+POS_SKPN:
	if (p->Pvalue == 1) {
	    b->Pop = P_SOS+POF_ISSKIP+POS_SKPL;
	    dropjump(q);
	    p->Pop = P_CAI+POF_ISSKIP+POS_SKPLE;
	    setskip (p);
	}
	break;

    case P_CAI+POF_ISSKIP+POS_SKPN:
	if (b->Ptype != PTA_RCONST) break;
	if (b->Pvalue + 1 == p->Pvalue) {
	    b->Pop = P_CAI+POF_ISSKIP+POS_SKPL;
	    dropjump(q);
	    p->Pop = P_CAI+POF_ISSKIP+POS_SKPLE;
	    setskip (p);
	} else if (b->Pvalue == p->Pvalue + 1) {
	    b->Pop = P_CAI+POF_ISSKIP+POS_SKPL;
	    b->Pvalue--;		/* note foldskip unsafe here! */
	    dropjump(q);		/* might be in a recursive casejump */
	    setskip (p);
	    p->Pop = P_CAI+POF_ISSKIP+POS_SKPLE;
	    p->Pvalue++;
	}
    }					/* end switch(b->Pop) */

    foldjump (lab);			/* common optimizations with P_JRST */
    if (!oneinstr (previous) || (p = before (previous)) == NULL) return;

    /* fold  P_JUMPx R,lab / instr / lab::  into  P_CAIx R,0 / instr */
    if (p->Pptr == lab) {
	jumptoskip (p);			/* make into skip */
	if (isskip (p->Pop)) setskip (previous); /* set instr skipped */
	crossjump (lab);		/* now retry pulling P_JUMPx across */
	p = before (previous);		/* fix up again */
    }

    /*
    ** Remaining optimizations work for jumps over two instructions.
    ** If we don't have that, give up.
    */

    if ((q = before (p)) == NULL || (q->Ptype & PTF_ADRMODE) != PTA_MINDEXED ||
	q->Pptr != lab) return;		/* make sure some jump to lab */

    /*
    ** fold:  P_JUMPx R,$n
    **	      P_MOVE S,R
    **	      op
    **	    $n::
    **
    ** into:  P_SKIPx S,R
    **	       op
    */

    if (p->Pop == P_MOVE && p->Ptype == PTA_REGIS && q->Preg == p->Pr2 &&
	!prevskips (q)) switch (q->Pop & POF_OPCODE) {
    case P_JUMP:
	p->Pop = q->Pop ^ (P_JUMP ^ P_SKIP ^ POF_ISSKIP);
	dropjump (q);
	setskip (previous);
	optlab (lab);			/* try again */
	return;				/* tail recursively */

    case P_AOJ:
	p->Pop = q->Pop ^ (P_AOJ ^ P_AOS ^ POF_ISSKIP);
	dropjump (q);
	setskip (previous);
	optlab (lab);			/* try again */
	return;				/* tail recursively */

    case P_SOJ:
	p->Pop = q->Pop ^ (P_SOJ ^ P_SOS ^ POF_ISSKIP);
	dropjump (q);
	setskip (previous);
	optlab (lab);			/* try again */
	return;				/* tail recursively */
    }

    /* A couple of these deal with skipping over P_MOVEMs */
    if (previous->Pop == P_MOVEM && p->Pop == P_MOVE && p->Preg == previous->Preg)
    {
	if (p->Ptype == PTV_IMMED && p->Pvalue == 1 && q->Pop == P_SOJ+POS_SKPN &&
	    !prevskips (q) && changereg (p->Preg, q->Preg, before (q))) {

	    /*
	    ** fold:  P_SOJN R,$x
	    **	      P_MOVEI S,1
	    **	      P_MOVEM S,y
	    **	    $x::
	    **
	    ** into:  P_CAIN R,1
	    **	      P_MOVEM R,y
	    */

	    dropjump (q);		/* flush P_SOJN */
	    p->Pop = P_CAI+POF_ISSKIP+POS_SKPN;	/* make skip comparison */
	    p->Ptype = PTA_RCONST;		/* immediate built in to P_CAI */
	    p->Pvalue = 1;		/* against one */
	    setskip (previous);		/* movem is now skipped over */
	    optlab (lab);		/* try again */
	    return;			/* tail recursively */
	}

	/*
	** fold:  P_MOVEM R,x
	**	  P_JUMPy S,lab
	**	  P_MOVE T,w
	**	  P_MOVEM T,x
	**
	** into:  P_CAIy S,0
	**	  P_MOVE R,y
	**	  P_MOVEM R,x
	*/

	for (b = q; b != NULL; b = before (b))
	    if (b->Pop == P_MOVEM) {		/* Find MOVEM if any */
		if (prevskips(b)) b = NULL;	/* Forget it if it's skipped */
		break;
	    }
	for (bb = after(b); b != NULL && bb != NULL && bb != p; bb = after(bb))
	    switch (bb->Pop & POF_OPCODE) {
		case P_MOVE: case P_ADD: case P_AOJ: case P_SOJ: case P_SKIP:
		case P_SETZ: case P_SETO: case P_IOR: case P_LDB:
		    if (bb->Preg == b->Preg)
			b = NULL;	 /* unsafe rchange, flush */
		case P_TLN: case P_TDN: case P_TRN:
		case P_CAI: case P_CAM: case P_JUMP:
		case P_JRST:
		    continue;		/* safe or not, try again or break */
		default: b = NULL;	/* not one we know */
	    }
	if (b != NULL && sameaddr(b, previous, 0)) {
	    jumptoskip (q);		/* turn jump into skip */
	    p->Preg = previous->Preg = b->Preg;	/* set registers */
	    setskip (p);		/* P_MOVE is now skipped over */
	    b->Pop = P_NOP;		/* drop first P_MOVEM */
	    if (q->Pop == P_TRN+POF_ISSKIP+POS_SKPA && invskip (before (q)))
		q->Pop = P_NOP;		/* fold P_TRNA into previous */
	    else foldskip(q, 0);	/* else safely fold ex-P_JUMP */
	    return;
	}
    }					/* end if P_MOVE and P_MOVEM */

    /*
    ** fold:  (skips)
    **	      P_JRST lab
    **	      P_MOVE R,y
    **	      instr
    **
    ** into:  (inverse skips)
    **	      P_SKIPA R,y
    **	      P_TRNA
    **	      instr
    */

    if (q->Pop != P_JRST || !newskip (p)) return;
    if (!invskip (before (q))) {
	unskip (p);			/* can't flip, undo changes */
	return;				/* and give up */
    }
    reflabel (lab, -1);			/* drop jump */
    setskip (p);			/* p skipped */
    q->Pop = P_TRN+POF_ISSKIP+POS_SKPA;		/* make P_TRNA */
    q->Ptype = PTA_ONEREG;			/* no mem ref */
    setskip (q);			/* q skipped */
    swappseudo (p, q);			/* swap P_TRNA and new P_SKIPA */
    setskip (previous);			/* previous skipped */

    if (!invskip (q)) return;		/* rest depends on flipping P_SKIPA */
    p->Pop = P_NOP;			/* if can flip, no P_TRNA */

    /*
    ** fold:  P_SKIPA R,x
    **        P_TDOA R,$BYTE
    **        P_IOR R,$BYTE
    **
    ** into:  P_MOVE R,x
    **	      P_IOR R,$BYTE
    */

    if (previous->Pop != P_IOR || (q = before (previous)) == NULL ||
	q->Pop != P_TDO+POF_ISSKIP+POS_SKPA || !sameaddr (q, previous, 0)) return;
    unskip (before (q));		/* turn prev skip off */
    q->Pop = P_NOP;			/* drop P_TDOA */
    clrskip (previous);			/* P_IOR not skipped over anymore */
}
/* UNJUMP(label) - Get rid of jump so can replace it with jumped-to label
**
** Called when we know there will only be one more instruction between
** here and the label, but we can't rely on later optimization because
** we want to emit another label here.
**
** Returns 1 or 0 depending on whether made successful skip.
** In neither case changes PTF_SKIPPED attribute of succeeding instrs.
*/

int
unjump (lab)
label lab;
{
    PCODE *p;

    if (previous == NULL || previous->Pptr != lab) return 0;
    switch (previous->Pop & POF_OPCODE) {
    case P_JRST:
	dropjump (previous);		/* drop it all the way out */
	return 1;

    case P_JUMP:  case P_AOJ:  case P_SOJ:
	jumptoskip (previous);		/* make into P_CAIx */
	previous->Pop = revop (previous->Pop); /* with opposite parity */
	return 1;			/* let optlab() take care of rest */

    default:
	return 0;
    }
}
/* FOLDTRNA(p) - Attempt to get rid of a P_TRNA.
**
** Folds it into instructions ahead of it in the buffer.
** This is called late, by realcode(), to avoid confusion
** in earlier parts of the peepholer.
*/

int
foldtrna (p)
PCODE *p;
{
    PCODE *q, *a;

    if (p == NULL || p->Pop != P_TRN+POF_ISSKIP+POS_SKPA || (q = after (p)) == NULL ||
	(q->Pop != P_JRST && q->Pop != P_POPJ && (q->Pop & POF_OPSKIP) != POS_SKPA) ||
	(a = after (q)) == NULL || !newskip (a)) return 0; /* no good */

    /*
    ** fold:  P_TRNA
    ** 	      P_JRST  $x
    ** 	      P_MOVE  R,y
    **
    ** into:  P_SKIPA R,y
    ** 	      P_JRST  $x
    */

    unskip (q);				/* turn P_SKIPA over op into P_MOVE */
    setskip (a);			/* op that skipped P_TRNA now skips it */
    swappseudo (q, a);			/* switch the jump and new skip */
    p->Pop = P_NOP;			/* P_TRNA disappears */
    return 1;				/* tell realcode() we won */
}
/* INSKIP(p) - Fold test type instruction into previous P_SKIPA+P_MOVE
*/
void
inskip (p)
PCODE *p;
{
    PCODE *q, *b;
    int skop, compl;

    if (p->Ptype != PTA_REGIS) return;	/* must be unskipped register op */

    switch (p->Pop) {			/* see what we want to fold back up */
    case P_AND: skop = P_TDZ; compl = 1; break; /* P_SKIPA+P_MOVE+P_AND => P_TDZA+P_AND */
    case P_IOR: skop = P_TDO; compl = 0; break; /* P_SKIPA+P_MOVE+P_IOR => P_TDOA+P_IOR */
    case P_XOR: skop = P_TDC; compl = 0; break; /* P_SKIPA+P_MOVE+P_XOR => P_TDCA+P_XOR */
    default: break;			/* can't handle anything else */
    }

    /* look for P_SKIPA and P_MOVE (with right registers) before op */
    /* we save the unsetz for last because it has side effects */
    if ((q = before (p)) == NULL || (q->Preg != p->Pr2 && q->Preg != p->Preg)
	|| (b = before (q)) == NULL || b->Pop != P_SKIP+POF_ISSKIP+POS_SKPA ||
(compl && b->Ptype != PTV_IMMED+PTF_SKIPPED) || b->Preg != q->Preg ||
	!unsetz (q)) return;

    /* found them, perform the fold */
    b->Pop = skop + POF_ISSKIP + POS_SKPA;	/* make always skipping op for P_SKIPA */
    if (b->Ptype & PTF_IMM) {		/* if immediate quantity */
	b->Ptype &=~ PTF_IMM;		/* make PTA_RCONST rather than PTV_IMMED */
	b->Pop = immedop (b->Pop);	/* and use built-in-immed op */
    }
    if (compl) b->Pvalue = ~ b->Pvalue;	/* complement mask if P_TDZA for P_AND */
    q->Pop = p->Pop;			/* and non-skip for P_MOVE (no compl) */

    /* see if result is already in right reg; if not, put it there */
    if (q->Preg == p->Pr2 || changereg (p->Preg, p->Pr2, before (b))) {
	b->Preg = q->Preg = p->Preg;	/* ok or fixed, change regs */
	p->Pop = P_NOP;			/* drop now useless op */
	fixprev();			/* point again to end of ph buff */
    } else {
	b->Preg = q->Preg = p->Pr2;	/* pre-op val in wrong reg, fix ops */
	p->Pop = P_MOVE;			/* add P_MOVE to put in right place */
    }
}