Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/old/kcc/cccode.c
There are 8 other files named cccode.c in the archive. Click here to see a list.
/*
** cccode -- Emit instructions into the peephole buffer for KCC
** (C) 1981  K. Chen
*/

#include "ccgen.h"

extern pcode before();			/* appease type checker */
void code7(), code8();			/* more of the same */

/*
** Get a new pseudo-code record.
** It is returned in previous.
*/

#define OVERINC	20			/* Number of ops to skip on overflow */

void newcode()
{
    int i;

    previous = &codes[maxcode++ & (MAXCODE - 1)]; /* new previous */
    if (maxcode >= mincode + MAXCODE - 1) /* overflow? */
	for (i = 0; i < OVERINC; i++) realcode(mincode++); /* yes, bump */
}
/*
** Register to register codes.
**
** Generates  op r1,r2  for r1 and r2 both registers.
** Register r2 is released for future reassignment (although later
** peephole optimizations might notice that it hasn't been changed yet
** and re-use whatever value it contained).
*/

void code0(op, r1, r2)
struct vreg *r1, *r2;
{
    int r = realreg (r1), s = realreg (r2);
    pcode p, q, b, bb;

    if (r1 != r2) release(r2);		/* flush operand for later */
    if (op != MOVM && optimize) {	/* don't do this for case hash */
	s = ufcreg (s);			/* flush failed changereg for 2nd AC */
	if ((op & OPCODE) == CAM) r = ufcreg (r); /* and other if comparison */
	if (op == MOVN && pushneg (s, previous)) op = MOVE; /* fold out MOVN */
	if (op == MOVE && changereg (r, s, previous)) return; /* and MOVE */

	p = previous;			/* initialize loop counter */
	while (p != NULL && !prevskips (p)) { /* looking at foldable ops */
	    if (p->preg != s) switch (op & OPCODE) {
	    case LDB:
	    case DPB:
		if (p->pop == IBP && (p->ptype != REGIS || p->pr2 != s)) {
		    p = before(p);	/* IBP X + DPB R,S - skip back over */
		    continue;		/* and try for MOVE before it */
		}
		break;

	    case CAM:
		if (p->pop == DMOVE && p->preg == s - 1 && !(p->ptype & IND)) {
		    p->pop = MOVE;	/* DMOVE S-1,x / CAMN R,S */
		    code7 (op, r, p->pptr, p->poffset+1, p->pindex);
		    return;		/* becomes MOVE S-1,x / CAMN R,x+1 */
		}

		if (isskip (p->pop) && p->preg != r && (q = before(p)) != NULL
		    && q->pop == MOVE && q->preg == s && !prevskips (q)) {

		    q->pop = revop (op);	/* fold:  MOVE S,x     */
		    q->preg = r;		/*        CAMN R-1,x-1 */
		    p->pop = revop (p->pop);	/*  	  CAME R,S     */
		    return;			/* into:  CAMN R,x     */
		}				/*  	  CAME R-1,x-1 */

		if (p->preg != r || p->pop != MOVE) break;
		r = s;			/* MOVE R,x / CAMx R,S => CAMx S,x */
		s = p->preg;		/* just switch registers */
		op = swapop(op);	/* and swap order of skip args */
		continue;		/* then loop back for rest of fold */

	    case DMOVE:
		if (p->preg == s + 1 && p->pop == SETZ) {
		    p->preg = r + 1;	/* fold SETZ s+1 / DMOVE r,s */
		    code0 (MOVE, r, s);	/* into SETZ r+1 / MOVE r,s */
		    return;
		}
		break;

	    case PUSH:
		if (p->preg == s + 1 && p->pop == SETZ && p->ptype == ONEREG) {
		    p = before (p);	/* fold push of float coerced */
		    continue;		/* into double */
		}
		break;

	    case SUB:	    case ADD:	    case IMUL:
	    case IOR:	    case AND:	    case XOR:
	    case FADR:	    case FSBR:	    case FMPR:	    case FDVR:
		switch (p->pop) {
		case SETZ:  case SETO:	case MOVE:  case MOVN:
		    if (p->preg == r) {
			if (p->ptype == IINDEXED) break;
			if (op == SUB || op == FSBR || op == FDVR) break;
			code0(op, s, r); /* reverse order */
			code0(MOVE, r, s); /* and put back in right register */
			return;
		    }
		case SUB:	    case ADD:	    case IMUL:
		case IOR:	    case AND:	    case XOR:
		case FADR:	    case FSBR:	    case FMPR:	    case FDVR:
		    if ((p->pop & BOTH) || p->pr2 == r || p->pr2 == s) break;
		case ADJSP:		/* handle case of despilled register */
		    p = before (p);
		    continue;
		}			/* end switch (p->pop) */
		break;
	    }				/* end if (p->preg != s) switch (op) */
	    else switch (p->pop & (OPCODE | BOTH)) {
	    case IMUL:
		if (op != SUB || (q = before (p)) == NULL || q->pop != IDIV ||
		    q->preg != p->preg || !sameaddr (p, q, 0) ||
		    (b = before (q)) == NULL || b->pop != MOVE ||
		    b->preg != p->preg) break;
		if (b->ptype == REGIS && b->pr2 == r) bb = NULL;
		else if ((bb = before (b)) == NULL || bb->preg != r ||
			 bb->pop != MOVE || prevskips (bb) ||
			 !sameaddr (b, bb, 0)) break;

		/*
		** fold:  MOVE R,x
		**  	  MOVE S,x
		**  	  IDIV S,y
		**  	  IMUL S,y
		**  	  SUB R,S
		**
		** into:  MOVE S,x
		**  	  IDIV S,y
		**  	  MOVE R,S+1
		**
		** for ignorant Pascal programmers.
		*/

		if (bb != NULL) bb->pop = NOP; /* drop first move */
		p->pop = NOP;		/* drop IMUL */
		fixprev();		/* point to end of peephole buffer */
		code0 (MOVE, r, s + 1);	/* put result in right reg */
		return;

	    case DMOVN:			/* similar to MOVN below */
		switch (op) {
		case DMOVE:
		    p->pop = DMOVE;	/* move MOVN over for optimization */
		    op = DMOVN;
		    break;
		case DMOVN:
		    p->pop = DMOVE;	/* cancel double MOVN */
		    op = DMOVE;
		    break;
		case DFAD:
		    p->pop = DMOVE;	/* R + -X is same as R - X */
		    op = DFSB;
		    break;
		case DFSB:
		    p->pop = DMOVE;	/* R - -X is same as R + X */
		    op = DFAD;
		    break;
		default:
		    p = NULL;		/* clear pointer */
		    continue;		/* to break out of loop */
		}
	    case DMOVE:
		switch (op) {
		case DFAD:
		case DFSB:
		case DFDV:
		case DFMP:
		case DMOVE:
		case DMOVN:
		    p->pop = op;
		    p->preg = r;
		    return;

		case PUSH:
		    if (p->ptype & IND) break; /* split DMOVE+PUSH */
		    p->pop = MOVE;	/* into MOVE+PUSH to save an instr */
		    p->preg++;		/* when we get to the other PUSH */
		    code7 (PUSH, r, p->pptr, p->poffset++, p->pindex);
		    if (previous->pop != PUSH) return;
		    p = before (previous); /* now pick up the pieces */
		    if (p->pop != MOVE) return; /* and shake them back into */
		    if (p->pindex == SP) p->poffset--; /* order for */
		    swappseudo (p, previous); /* later optimization */
		    return;
		}
		break;

	    case IOR:
	    case ADJBP:
		if (op != LDB && op != DPB) break; /* not byte op, give up */
		if (localbyte(op, r, s)) return; /* full local byte ptr */
		if (p->pop != ADJBP && p->pop != IOR) {
		    p = previous;	/* changed by localbyte, restart */
		    continue;		/* go back for more opt */
		}
		break;

	    case SETZ:
		switch (op) {
		case MOVE:		/* fold:  SETZ S,   */
		case IMUL:		/*        MOVE R,S  */
		case FMPR:
		case MOVN:		/* into:  SETZ R,   */
		case FLTR:
		    p->preg = r;
		    return;

		case ADD:		/* fold:  SETZ S,   */
		case SUB:		/*        ADD  R,S  */
		case FADR:
		case FSBR:		/* into:  null      */
		    p->pop = NOP;	/* flush the SETZ */
		    fixprev();
		    return;
		}

	    case SETO:			/* fall in from SETZ above */
		p->pvalue = (p->pop == SETO? 1 : 0);
		p->pop = MOVN;
		p->ptype = IMMED;

	    case MOVN:
		/* invert MOVN(*) to MOVE(*) for following optimization */

		switch(op & OPCODE) {
		case MOVE:
		    p->pop = MOVE;	/* move MOVN over for optimization */
		    op = MOVN;
		    break;
		case MOVN:
		    p->pop = MOVE;	/* cancel double MOVN */
		    op = MOVE;
		    if (changereg (r, s, previous)) return;
		    break;
		case ADD:
		    p->pop = MOVE;	/* R + -X is same as R - X */
		    op = SUB;
		    break;
		case SUB:
		    p->pop = MOVE;	/* R - -X is same as R + X */
		    op = ADD;
		    break;
		case FADR:
		    p->pop = MOVE;	/* R + -X is same as R - X */
		    op = FSBR;
		    break;
		case FSBR:
		    p->pop = MOVE;	/* R - -X is same as R + X */
		    op = FADR;
		    break;
		case CAM:  case IMUL:  case FMPR:  case FDVR:
		    if (p->ptype != IMMED && (q = before(p)) != NULL &&
			q->preg == r && !prevskips (q)) switch (q->pop) {
		    case MOVE:
			q->pop = MOVN;	/* a <= -b is same as -a >= b */
			p->pop = MOVE;
			op = swapop(op);
			continue;
		    case MOVN:
			q->pop = MOVE;	/* -a <= -b is same as a >= b */
			p->pop = MOVE;
			op = swapop(op);
			continue;
		    }
		default:
		    if (p->ptype == IMMED) {
			p->pop = MOVE;	/* unknown const case, make MOVE */
			p->pvalue = - p->pvalue; /* with negated value */
		    } else {
			p = NULL;	/* clear out counter */
			continue;	/* to break out of loop */
		    }
		}

	    case MOVE:

		/*
		** fold:  MOVE  S,x
		**        OP    R,S
		**
		** into:  OP    R,x
		*/

		/* don't make an XPUSHI - foldstack works better without it */
		if (p->ptype == IINDEXED && op == PUSH) break;

		p->preg = r;		/* hack the new register in */

		/* CAML for an immediate type needs to become CAIL... */
		if ((p->pop = immedop(op)) != NOP && (p->ptype & IMM)) {
		    p->ptype &=~ IMM;	/* make RCONST instead of IMMED */
		    foldskip (previous, 1); /* fix up CAI */
		    return;
		}

		q = before(p);		/* look back before munged move */
		switch ((p->pop = op) & OPCODE) { /* set op, see what it was */
		case PUSH:
		    code8 (ADJSP, SP, 0); /* try adjustment */
		    break;

		case SKIP:
		    if (p->pop == SKIP+ISSKIP+SKPE && p->ptype == IINDEXED)
			p->pop = MOVE;	/* (char *) of imm addr neednt check */
		    break;		/* to make sure it is non-NULL */

		case FLTR:
		    if (p->ptype != IMMED) return;
		    p->pop = MOVE;	/* fold: FLTRI R,x */
		    p->ptype = PFLOAT;	/* into: MOVSI R,(xE0) */
		    p->pmantissa = p->pvalue;
		    p->pexponent = 0;
		    return;

		case IMUL:
		    if (p->ptype != IMMED) return;
		    if (p->pvalue == 1) {
			p->pop = NOP;	/* drop IMULI R,1 */
			fixprev();	/* back to end of peephole buffer */
			return;
		    }

		    if (q->ptype == IMMED /* !prevskips */ && q->preg == r)
		    switch(q->pop) {
		    case SUB: case ADD:

			/*
			** fold:  ADDI   R,n
			**        IMULI  R,m
			**
			** into:  IMULI  R,m
			**        ADDI   R,m*n
			*/

			q->pvalue *= p->pvalue;	/* premultiply constant */
			swappseudo (p, q); /* put add after multiply */
			p = q;		/* look at multiply for below */
			q = before(p);	/* and before in case another mult */
			if (q->pop != IMUL || q->ptype != IMMED ||
			    q->preg != r) break; /* check safe to drop in */
		    case IMUL:

			/*
			** fold:  IMULI  R,n
			**        IMULI  R,m
			**
			** into:  IMULI  R,n*m
			*/

			q->pvalue *= p->pvalue;	/* multiply both together */
			p->pop = NOP;	/* only keep earlier one */
			fixprev();	/* point to end of peephole buffer */
			return;

		    case MOVN: case MOVE:

			/*
			** fold:  MOVEI  R,n
			**        IMULI  R,m
			**
			** into:  MOVEI  R,m*n
			*/

			q->pvalue *= p->pvalue;	/* mult const by factor */
			p->pop = NOP;	/* flush folded multiply */
			fixprev();	/* point to end of peephole buffer */
			return;
		    }
		    break;

		case LDB:
		case DPB:
		    if (p->ptype == BYTEPOINT) switch (p->pindex & 0777700) {

		    /*
		    ** fold:  LDB  R,[2200,,x]
		    ** into:  HRRZ R,x
		    */

		    case 0222200:	/* left half? */
			p->ptype = MINDEXED;
			if (p->pindex & 020) p->ptype |= IND;
			p->pindex &= 017;
			p->pop = (p->pop == LDB)? HLRZ : HRLM;
			break;

		    case 02200:		/* right half? */
			p->ptype = MINDEXED;
			if (p->pindex & 020) p->ptype |= IND;
			p->pindex &= 017;
			p->pop = (p->pop == LDB)? HRRZ : HRRM;
		    }
		    foldmove(p);	/* look for IBP to fold into */
		    return;		/* ILDB or IDPB */

		case ADJBP:
		    foldbyte(p);	/* fix up ADJBP instruction */
		    return;

		case SUB:
		    if (p->ptype == IMMED && q->preg == r &&
			(q->ptype == IMMED || q->ptype == IINDEXED))
		    switch (q->pop) {	/* check safe then switch */
		    case MOVE:
		    case ADD:
			q->poffset -= p->poffset;
			p->pop = NOP;	/* fold:  ADDI R,n */
			fixprev();	/*  	  SUBI R,m */
			foldplus(q);	/* into:  ADDI R,n-m */
			return;

		    case SUB:
			q->poffset += p->poffset;
			p->pop = NOP;	/* fold:  SUBI R,n */
			fixprev();	/*  	  SUBI R,m */
			return;		/* into:  SUBI R,n+m */
		    }			/* fall through to foldplus() */
		case ADD:
		    foldplus(p);	/* do general optimization on add */
		    return;

		case CAM:
		    if (q != NULL && q->ptype == REGIS /* !prevskips */ &&
			q->pop == MOVE && q->preg == p->preg) {

			/*
			** fold:  MOVE  R,S
			**        CAMx  R,x
			**
			** into:  CAMx  S,x
			*/

			p->preg = q->pr2; /* flatten tested register */
			q->pop = NOP;	/* flush useless move */
		    }
		}
		return;			/* end of case for p->pop == MOVE */

	    case ADD:
	    case SUB:

		/*
		** fold:  ADD S,x
		**        ADD R,S
		**
		** into:  ADD R,S
		**        ADD R,x
		**
		** and keep looking back for further optimization.
		** (small lie above: the ADD R,S if generated will come last)
		*/

		if (op == SUB) p->pop ^= (ADD ^ SUB);
		else if (op != ADD) break;
		p->preg = r;
		p = before(p);
		continue;

	    case SETZ+BOTH: case SETO+BOTH:
		if (p->ptype == REGIS && (op == DMOVE || op == DMOVN) &&
		    p->preg == s && p->pr2 == s + 1) {

		    /*
		    ** fold:  SETZB S,S+1
		    **        DMOVE R,S
		    **
		    ** into:  SETZB R,R+1
		    */

		    p->preg = r;
		    p->pr2 = r + 1;
		    return;
		}
	    }				/* end switch (p->pop&(OPCODE|BOTH)) */
	    break;			/* escape otherwise inf while loop */
	}				/* end while (p != NULL) */
    }					/* end if (op != MOVM && optimize) */

    /* all optimization failed, add the actual instruction */
    newcode();
    previous->ptype = REGIS;
    previous->pop = op;
    previous->pr1 = r;
    previous->pr2 = s;

    /* post-optimization */
    if (!optimize) return;		/* no optimization means all done */
    if (op != PUSH && op != MOVE)	/* not if it's a bad idea, but */
	findconst (previous);		/* turn CAMN R,S into CAIN R,0 */
    switch (op & OPCODE) {
    case PUSH:
	code8 (ADJSP, SP, 0);		/* hack up stack */
	break;

    case AND: case IOR: case XOR:
	inskip (previous);		/* turn SKIPA+MOVE+IOR into TLOA+IOR */
	break;

    case CAM:
	foldskip (previous);
	break;

    case ADJBP:
	foldbyte(previous);		/* fix up ADJBP instruction */
	break;

    case ADD:
	foldplus(previous);		/* maybe addition can be fixed now */
	break;

    case MOVE: case LDB: case DPB:
	foldmove(previous);		/* fix IBP+DPB, copy of reg-reg move */
	break;
    }
}
/*
** Immediate register constant codes.
** Generates  opI r,s  where s is a number (ptype IMMED).
*/

void code1(op, r, s)
{
    r = realreg(r);
    newcode();
    previous->ptype = IMMED;
    previous->pop = op;
    previous->preg = r;
    previous->poffset = s;
    if (optimize) {
	foldplus(previous);		/* now do post-optimizations */
	foldmove(previous);
    }
}
/*
** Local byte pointer
**
** Generates  op r,[bbbbii,,p+o]  i.e. a local byte pointer with p/s in b.
** Used in optimization of string ops, and in generating struct bit fields.
*/

void code2(op, reg, b, idx, p, o)
struct vreg *reg, *idx;
struct SYMBOL *p;
{
    int r = realreg(reg), i = realreg(idx);

    /* look for fold with earlier instruction */
    if (previous != NULL && previous->preg == i && optimize &&
	!prevskips (previous)) switch (previous->pop) {
    case ADD:
	if (previous->ptype == IMMED) {
	    o += previous->pvalue;	/* this byte pointer has an offset */
	    previous->pop = NOP;	/* drop it */
	    fixprev();
	}
	break;

    case SUB:
	if (previous->ptype == IMMED) {
	    o -= previous->pvalue;	/* this byte pointer has an offset */
	    previous->pop = NOP;	/* drop it */
	    fixprev();
	}
	break;

    case MOVE:
	if (p != NULL) break;		/* don't double up symbol */
	if (previous->ptype == MINDEXED) {
	    if (o != 0) break;		/* no offset from indirect */
	    b |= 020;			/* allow indirect */
	} else if (previous->ptype != IINDEXED) break;
	p = previous->pptr;		/* find new address name */
	i = previous->pindex;		/* index */
	o += previous->poffset;		/* and offset */
	previous->pop = NOP;		/* drop the move */
	fixprev();			/* and clean up after it */
    }

    /* optimizations done, now make the byte pointer op */
    newcode();
    previous->ptype = BYTEPOINT;
    previous->pop = op;
    previous->preg = r;
    previous->pindex = b | i;
    previous->pptr = p;
    previous->poffset = o;
    foldmove(previous);			/* look for another like that */
}
/*
** Address immediate -> register codes.
**
** Generates  opI r,s  with s a symbol (ptype IINDEXED).  If left unoptimized
** this will be generated as  XMOVEI 16,s  /  op r,16.
*/

void code3(op, r, s)
struct SYMBOL *s;
{
    r = realreg(r);
    newcode();
    previous->ptype = IINDEXED;
    previous->pop = op;
    previous->preg = r;
    previous->pptr = s;
    previous->poffset = 0;
    previous->pindex = 0;
}
/*
** Register to indexed register.
**
** Generates  op reg,(idx)  i.e. indexes through the second register.
** This is used after a gaddress() to get the contents of a variable,
** and also for assignment ops (like ASGN = MOVEM).  The index is released.
*/

void code4(op, reg, idx)
struct vreg *reg, *idx;
{
    int r = realreg(reg), s = ufcreg(realreg(idx));
    pcode b;
    struct SYMBOL *ssymbol;
    int ty, o, soffset;

    if (reg != idx) release(idx);	/* will no longer need register */
    if ((op == SPUSH || op == SPOP) && optimize) r = ufcreg (r);

    /* allow fold even if we did something innocuous first */
    if (previous->ptype == IMMED /* !prevskips */ && previous->preg == r &&
	previous->pop == MOVE && (b = before(previous)) != NULL &&
	b->preg == s && r != s && !prevskips (b)) swappseudo(b,previous);

    if (previous != NULL && previous->preg == s && optimize) {
	switch (previous->ptype /* incl SKIPPED for prevskips chk */) {
	case IINDEXED:
	    switch (previous->pop) {
	    case ADD:

		/*
		** fold:  ADDI   S,addr(I)
		**        OP     R,(S)
		**
		** into:  MOVEI  S,addr(S)
		**  	  ADD    S,I
		**        OP     R,(S)
		**
		** and optimize further...
		*/

		if ((o = previous->pindex) != 0 && o == r) break; /* give up */
		previous->pop = MOVE;	/* change to XMOVEI */
		s = previous->pindex = foldcse (s, before (previous));
		if (o) code0 (ADD, previous->preg, o); /* followed by ADD */
		code4 (op, r, s);	/* then try code4 again */
		return;

	    case MOVE:

		/*
		** fold:  XMOVEI S,addr
		**        OP     R,(S)
		**
		** into:  OP     R,addr
		*/

		previous->ptype = MINDEXED;
		previous->pop = op;
		previous->preg = r;
		foldboth();
		return;
	    }
	    break;

	case IMMED:
	    switch (previous->pop) {
	    case SUB:
		previous->poffset = - previous->poffset;
	    case ADD:

		/* fold:  ADDI   S,offset    */
		/*        OP     R,@S        */
		/* into:  OP     R,offset(S) */

		previous->ptype = MINDEXED;
		previous->pptr = NULL;
		previous->pop = op;
		previous->preg = r;
		if ((b = before(previous)) != NULL && b->ptype == REGIS &&
		    b->pop == MOVE && b->preg == s) {
		    s = b->pr2;		/* flatten losing changereg */
		    b->pop = NOP;	/* and drop silly move */
		}
		previous->pindex = foldcse(s, before(previous));
		foldboth();
		return;

	    case MOVN:

		/* fold:  MOVNI  S,const  */
		/*        OP     R,@S     */
		/*			  */
		/* into:  OP     R,-const */

		previous->pvalue = - previous->pvalue;

	    case MOVE:

		/* fold:   MOVEI  S,const  */
		/*         OP     R,@S     */
		/*			   */
		/* into:   OP     R,const  */

		previous->ptype = RCONST;
		previous->preg = r;
		previous->pop = op;
		return;
	    }
	}
    }

    /* no optimizations found, just do the change */
    s = foldcse(s, previous);		/* fold out index expression */
    newcode();
    previous->ptype = MINDEXED;
    previous->pptr = NULL;
    previous->pop = op;
    previous->preg = r;
    previous->poffset = 0;
    previous->pindex = s;
    if (optimize) foldboth();		/* check remaining optimizations */
}
/*
** single register codes
**
** code5 (op, r)
** generates  op r,  for ops like SETZ.
*/

void code5(op, reg)
struct vreg *reg;
{
    int r = realreg (reg);		/* get number for register */

    if (optimize) switch (op) {
    case SETZ: case SETO:
	if (previous == NULL || previous->pop != op ||
	    previous->ptype != ONEREG /* !prevskips */) break;

	/*
	** fold:  SETZ  S,
	**  	  SETZ	R,
	**
	** into:  SETZB S,R
	*/

	previous->ptype = REGIS;
	previous->pop |= BOTH;
	previous->pr2 = r;
	return;

    case DFLOT:				/* zero needs no normalization */
	if (previous != NULL && previous->preg == r && previous->pr2 == r+1 &&
	    previous->pop == SETZ+BOTH && previous->ptype == REGIS) return;
    }

    newcode();
    previous->ptype = ONEREG;
    previous->pop = op;
    previous->pr1 = r;
    if (op == SETZ) foldmove (previous);
}
/*
** Codes involving local labels.
** Generates  op r,$s.  This is mostly used for jumps.
*/

void code6 (op, reg, s)
struct SYMBOL *s;
struct vreg *reg;
{
    int r = realreg (reg), skipped;
    pcode p, q;

    if ((op & OPCODE) == JUMP && previous != NULL && !isskip (previous->pop)) {

	/*
	** emit  JUMPx R,lab
	** as    CAIx  R,0
	** 	 JRST  lab
	**
	** So optimizer can work best.  If it doesn't get folded,
	** then further along in this routine this will get undone.
	*/

	r = ufcreg (r);			/* code8 optimization loses w/o this */
	code8 (op ^ (CAI ^ JUMP ^ ISSKIP ^ INVSKIP), r, 0); /* compare to 0 */
	foldskip (previous, 1);		/* call peepholer */
	op = JRST;			/* make it a jump */
	r = 0;				/* JRST doesn't take a register */
    }

    if (op == JRST && optimize) foldjump (s); /* cascade and such */
    if (op == JRST && safejump(previous)) return; /* flush dead JRST */
    reflabel (s, 1);			/* adjust reference count for new op */

    if (op == JRST && optimize && previous != NULL && !prevskips (previous))
    switch (previous->pop & OPCODE) {
    case CAI:
        if (previous->ptype != RCONST || previous->pvalue != 0) break;

	/*
	** fold:  CAIx  R,0
	**	  JRST  lab
	**
	** into:  JUMPx R,lab
	*/

	op = (previous->pop ^ (CAI ^ JUMP ^ ISSKIP ^ INVSKIP));	/* invert */
	r = previous->preg;		/* get register from skip compare */
	previous->pop = NOP;		/* flush the compare */
	fixprev();
	break;

    case AOS:
	if (previous->ptype != REGIS) break;
	op = (previous->pop ^ (AOS ^ AOJ ^ ISSKIP ^ INVSKIP));
	r = previous->pr2;
	previous->pop = NOP;
	fixprev();
	break;

    case SOS:
	if (previous->ptype != REGIS) break;
	op = (previous->pop ^ (SOS ^ SOJ ^ ISSKIP ^ INVSKIP));
	r = previous->pr2;
	previous->pop = NOP;
	fixprev();
	break;

    case CAM:

	/*
	** fold:  ADDI R,1
	**  	  CAMN R,x
	**  	   JRST $y
	**
	** into:  SUB R,x
	**  	  AOJE R,$y
	*/

	if (previous->pop & CMPSKIP) break; /* only CAMN and CAME */
	for (p = before (previous);
	     p != NULL && p->pop == MOVE && p->preg != previous->preg;
	     p = before (p)) ;		/* skip over struct finding */
	if (p == NULL || p->preg != previous->preg || p->ptype != IMMED ||
	    p->pvalue != 1 || prevskips (p)) break; /* look for OPI R,1 */
	if (p->pop == ADD) op = previous->pop ^ (CAM ^ AOJ ^ ISSKIP ^ INVSKIP);
	else if (p->pop != SUB) break;	/* must be ADDI or SUBI */
	else op = previous->pop ^ (CAM ^ SOJ ^ ISSKIP ^ INVSKIP); /* now SOJ */
	r = p->preg;			/* use this register */
	p->pop = NOP;			/* drop ADDI or SUBI */
	previous->pop = SUB;		/* make SUB */
	if ((p = before (p)) == NULL || p->pop != JRST ||
	    (q = before (p)) == NULL || (q->pop & (OPCODE + CMPSKIP)) != CAM ||
	    q->preg != r || !sameaddr (q, previous, 0) || prevskips (q)) break;

	/*
	** fold:  CAMN R,x
	**  	  JRST $y
	**  	  SUB R,x
	**  	  AOJE R,$z
	**
	** into:  SUB R,x
	**  	  JUMPE R,$y
	**  	  AOJE R,$z
	*/

	p->preg = r;			/* make JUMPE */
	p->pop = q->pop ^ (CAM ^ JUMP ^ ISSKIP ^ INVSKIP);
	clrskip (p);			/* not skipped */
	q->pop = SUB;			/* make sub */
	previous->pop = NOP;		/* drop duplicated SUB */
	fixprev();			/* point to new peephole buf end */
    }

    /* all set up, drop values into the node */
    skipped = (previous != NULL && isskip (previous->pop));
    newcode();
    previous->ptype = MINDEXED;
    previous->pop = op;
    previous->preg = r;
    previous->pptr = s;
    previous->pindex = 0;
    previous->poffset = 0;
    if (skipped) setskip (previous);
}
/*
** General indexed instruction.
**
** Generates  op preg,pptr+poffset(pindex)
** I.e. any complicated op with type MINDEXED.
** This is used to duplicate addressing of an already-generated op.
*/

void code7 (op, preg, pptr, poffset, pindex)
struct vreg *preg, *pindex;
struct SYMBOL *pptr;
{
    int rreg = realreg(preg), rindex = realreg(pindex), nreg;
    if (rreg != rindex) release (pindex);

    /* maybe do more complicated optimizing code generation */
    if (pptr == NULL && poffset == 0) {
	code4 (op, rreg, rindex);
	return;
    }
    nreg = ufcreg (rreg);		/* undo failed changereg */

    /* too general for optimization, just add the code */
    newcode();
    previous->ptype = MINDEXED;
    previous->pop = op;
    previous->preg = nreg;
    previous->pptr = pptr;
    previous->poffset = poffset;
    previous->pindex = rindex;

    if (nreg != rreg && op != SPOP) code0 (MOVE, rreg, nreg);
}
/*
** Register-constant codes.
**
** Like code1(), but op doesn't become immediate, making type RCONST.
** E.g. code8 (ADJSP, SP, n) where n is some stack offset.
*/

void code8 (op, reg, val)
struct vreg *reg;
{
    int r = realreg (reg);
    pcode p;

    if (optimize) switch (op) {
    case ADJSP:
	while (previous != NULL && previous->pop == PUSH && val < 0) {
	    previous->pop = NOP;	/* drop lost push */
	    fixprev();			/* point to new end of peephole buff */
	    val++;			/* account for it in adjustment */
	}
	val = foldstack (val);
	if (val == 0) return;
	break;

    case TLC:
        if (val != 0243000) break;	/* make sure int=>double coercion */
    case ASHC:				/* const zero needs no coercion */
	if (previous != NULL && previous->preg == r && previous->pr2 == r+1 &&
	    previous->ptype == REGIS && previous->pop == SETZ+BOTH) return;
    }

    newcode();
    previous->ptype = RCONST;
    previous->pop = op;
    previous->preg = r;
    previous->pvalue = val;
    foldskip (previous, 0);		/* could be from switch, be careful */

    if (op == ADJSP && (p = before (previous)) != NULL && p->ptype == IINDEXED)
    {
	if (p->pindex == SP) p->poffset -= val;	/* adj stack addr for swap */
	swappseudo (p, previous);	/* get out of way of tail recurse */
    }
}
/*
** Floating point.
** Generates  op r,[mantissa E exponent]  i.e. floating point literal.
*/

void code9 (op, r, mantissa, exponent)
{
    r = realreg (r);
    if (op == MOVE && mantissa == 0 && optimize) { /* float zero */
	code5 (SETZ, r);		/* is same as integer zero */
	return;
    }
    newcode();
    previous->ptype = PFLOAT;
    previous->pop = op;
    previous->preg = r;
    previous->pmantissa = mantissa;
    previous->pexponent = exponent;
    if (optimize) foldmove (previous);	/* see if already have this one */
}
/*
** Indexed immediate.
**
** Generates  opI r,(s)
** Called by pctopi() to make an XMOVEI and flatten the byte pointer.
*/

void code11 (op, r, s)
{
    r = realreg (r);
    s = ufcreg (realreg (s));
    newcode();
    previous->ptype = IINDEXED;
    previous->pop = op;
    previous->preg = r;
    previous->pptr = NULL;
    previous->poffset = 0;
    previous->pindex = s;
}
/*
** Local, from memory.
**
** Generates  op r,s(17)
** This is used by the register allocation code to despill registers.
*/

void code12 (op, r, offset)
{
    r = realreg(r);
    newcode();
    previous->ptype = MINDEXED;
    previous->pop = op;
    previous->preg = r;
    previous->pptr = NULL;
    previous->poffset = offset;
    previous->pindex = SP;
}
/*
** Local, immediate.
**
** Generates  opI r,s(17)  i.e. an XMOVEI of a stack location.
** As far as I know the only op used is XMOVEI.
*/

void code13 (op, r, offset)
{
    r = realreg(r);
    newcode();
    previous->ptype = IINDEXED;
    previous->pop = op;
    previous->preg = r;
    previous->pptr = NULL;
    previous->poffset = offset;
    previous->pindex = SP;
}
/*
** Indirect indexed local label
**
** Generates  op @$lab+off(ind)
** Used with op=JRST for switch jump tables.
*/

void code15 (op, lab, off, idx)
struct SYMBOL *lab;
struct vreg *idx;
{
    int i = realreg(idx);
    newcode();
    previous->ptype = MINDEXED+IND;
    previous->pop = op;
    previous->preg = 0;
    previous->pptr = lab;
    previous->poffset = off;
    previous->pindex = i;
}
/*
** Indexed local label.
** Generates  op r,$lab(q).  Used for checking switch hash tables.
*/

void code16 (op, r, lab, q)
struct SYMBOL *lab;
{
    r = realreg(r);
    q = realreg(q);
    newcode();
    previous->ptype = MINDEXED;
    previous->pop = op;
    previous->preg = r;
    previous->pptr = lab;
    previous->poffset = 0;
    previous->pindex = q;
}
/*
** Simple value.
** Generates a literal value.  Used for generating switch hash tables.
*/

code17(value)
{
    newcode();
    previous->ptype = RCONST;
    previous->preg = 0;
    previous->pop = CVALUE;
    previous->pvalue = value;
}