Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/old/kcc/cccreg.c
There are 8 other files named cccreg.c in the archive. Click here to see a list.
/*
** KCC peephole optimizer functions for retroactively changing registers
** David Eppstein / Stanford University / 2 July 1985
*/

#include "ccgen.h"			/* get pseudo code defs */
#include "cccreg.h"			/* and enum rmod */

extern pcode before(), after();		/* declare functions we use */


/*
** Calculate effect of op on reg.
**
** rchange (op)
**    returns an enum rmod, as described above, describing the given opcode.
**    It doesn't care about the skips or other flags in the opcode word,
**    but it strips them off so the caller does not need to do so.
*/

enum rmod
rchange (op)
{
    switch (op & OPCODE) {
    case CAI:	case CAM:   case DPB:	case HRLM:  case HRRM:	case IBP:
    case IDPB:	case JRST:  case JUMP:  case MOVEM: case TDN:   case TLN:
    case TRN:
	return REG_SAME;

    case AOS:	case BPCNT: case FIXR:  case FLTR:  case HLRZ: 	case HRRZ:
    case ILDB:  case LDB:   case MOVE:  case MOVM:  case MOVN: 	case MOVS:
    case SETCM: case SETO:  case SETZ:  case SKIP:  case SOS:
	return REG_SET;

    case DFIX:
	return REG_SET_DBL;

    case ADD:	case ADJBP: case ADJSP:	case AND:   case AOJ:	case ASH:
    case ASHC:	case DFLOT: case EQV:	case FADR:  case FDVR:	case FMPR:
    case FSBR:	case FSC:   case IMUL:  case IOR:   case LSH:   case ORCM:
    case POP:	case POPJ:  case PUSH:  case ROT:   case SOJ:   case SPOP:
    case SPUSH: case SUB:   case SUBBP: case TDC:   case TDO:   case TDZ:
    case TLC: 	case TLO:   case TLZ:   case TRC:   case TRO:   case TRZ:
    case XOR:
	return REG_CHANGED;

    case DMOVEM:
	return DBL_SAME;

    case DMOVE:	case DMOVN:
	return DBL_SET;

    case DFAD:	case DFDV:  case DFMP:	case DFSB:
	return DBL_CHANGED;

    case IDIV:
	return DBL_CHG_SGL;

    case PUSHJ:
    default:
	return UNKNOWN_CHANGES;
    }
}
/*
** Change register for ADDI R,1
** Called by changereg() for REG_CHANGED when recursive change fails.
**
** We know that p->preg == from, but little else.
*/

craddhack (to, from, p)
pcode p;
{
    if (p->ptype != IMMED || p->pvalue != 1) return 0; /* pretest failed */
    switch (p->pop) {			/* is opI R,1; see if ADDI or SUBI */
    case ADD:
	p->pop = AOS;			/* ADDI R,1 */
	break;				/* becomes AOS S,R */

    case SUB:
	p->pop = SOS;			/* similarly for SUBI */
	break;

    default:				/* something else */
	return 0;			/* we can't handle it */
    }

    /* Here if was ADDI R,1 or SUBI R,1.  Finish the transformation. */
    p->ptype = REGIS;			/* make a reg-reg AOS */
    p->preg = to;			/* into new register */
    p->pr2 = from;			/* from old register */
    return 1;				/* return success */
}
/*
** Make sure changereg is final for skipped over op
**
** Called by changereg() when we have some op that is REG_SET but
** which can be skipped over.  If the control flow leading to the
** following op is never going to escape to the end of the current
** peephole buffer contents, or if the following op also sets the
** same register (and thus will be changed by the instance of
** changereg() which called the one that is calling us) we are safe.
**
** Arguments are the op after the one that was skipped over, and
** the register to be changed from.
*/

static int
cregok (p, r)
pcode p;
{
    if (p == NULL) return 0;
    if (rchange (p->pop) == REG_SET && p->preg == r) return 1;
    return dropsout (p);
}
/*
** Worker routine for changereg.
**
** This has the same calling conventions as changereg(), with the addition
** of two more arguments: dguard and dstart.  These are used to change
** registers for doubleword operations.
**
** It is mutually recursive with cregbefore().
*/

creg (to, from, p, dguard, dstart)
pcode p, dguard, dstart;
{
    if (to == from) return 1;		/* already right */
    if (p == NULL) return 0;		/* nothing to change */

    if (dropsout (p)) {			/* in an alternate universe? */
	if (from == RETVAL) return 0;	/* want this register saved, lose */
	return cregbefore (to, from, p, dguard, dstart);
    }

    switch (rchange (p->pop)) {		/* else classify op by reg changes */
    case REG_SET:
    case REG_SET_DBL:
	if (p->preg == to) return 0;	/* conflict, lose */
	if (p->preg != from) return cregbefore (to, from, p, dguard, dstart);
	if (!prevskips (p) || cregok (after (p), from)) { /* make sure ok */
	    if (dguard != NULL) return 0; /* right reg num, wrong reg val */
	    if (p->pop == MOVE && (p->ptype &~ SKIPPED) == REGIS &&
		to == p->pr2) {		/* old failed changereg? */
		p->pop = NOP;		/* yes, drop it */
		if (p->ptype & SKIPPED) unskip (before (p)); /* and prv skip */
	    } else p->preg = to;	/* otherwise just make change */
	    return 1;			/* and return winnitude */
	}				/* otherwise treat as REG_CHANGED */
    case REG_CHANGED:
	if (p->ptype == REGIS && p->preg == from && p->pr2 == to &&
	    dguard == NULL) switch (p->pop) {
	case ADD: case IMUL: case IOR: case AND: case XOR:
	case FADR: case FMPR:

	    /*
	    ** code0, when it sees
	    ** 	    OP1 R,x
	    ** 	    OP2 R,S
	    ** for some commutative op2, turns it into
	    ** 	    OP1 R,x
	    ** 	    OP2 S,R
	    ** 	    MOVE R,S
	    ** in the hope that OP2 can fold into OP1.  We come here
	    ** when that has not happened and we are trying not to
	    ** emit the MOVE - if that is the case we simply switch
	    ** the two registers in OP2.
	    */

	    p->preg = to;
	    p->pr2 = from;
	    return 1;
	}

    case REG_SAME:
    case REG_CHG_DBL:
	if (p->preg == to) return 0;	/* conflict, lose */
	if (p->preg != from) return cregbefore (to, from, p, dguard, dstart);
	if (!cregbefore (to, from, p, dguard, dstart))
	    return dguard == NULL? craddhack (to, from, p) : 0;
	p->preg = to;			/* changed up to here so change here */
	return 1;			/* and pass success back up */

    case DBL_CHG_SGL:			/* IDIV */
	if (dguard == p) {		/* already been here once? */
	    if (p->preg != from) to = to - 1; /* yes, change right reg */
	    if (!cregbefore (to, p->preg, p, NULL, before (p))) return 0;
	    p->preg = to;		/* done change before and here */
	    return 1;			/* so return success */
	}
	if (p->preg == to || p->preg == to - 1) return 0; /* blocked */
	if (dguard != NULL && (p->preg == from || p->preg == from - 1))
	    return 0;			/* can't deal with two at once */
	if (p->preg == from) {		/* first half of double chg */
	    if (to >= MAXREG || !rfree (from + 1)) return 0; /* check safe */
	    return creg (to + 1, from + 1, dstart, p, NULL); /* recurse */
	}
	if (p->preg == from - 1) {	/* same but with other reg */
	    if (to <= 1 || !rfree (from - 1)) return 0;	/* check safe */
	    return creg (to - 1, from - 1, dstart, p, NULL); /* recurse */
	}
	return cregbefore (to, from, p, dguard, dstart); /* normal, continue */

    case DBL_SAME:			/* can't deal with doublewords */
    case DBL_SET:			/* so if it uses any of our regs */
    case DBL_SET_SGL:			/* then we can't do anything. */
    case DBL_CHANGED:			/* otherwise we can ignore the op. */
	if (p->preg == to || p->preg == to - 1 || /* blocked */
	    p->preg == from || p->preg == from - 1) return 0; /* or bad op */
	return cregbefore (to, from, p, dguard, dstart); /* normal, continue */

    default:				/* UNKNOWN_CHANGES or worse */
	return 0;			/* give it up */
    }
}
/*
** Change register retroactively before op.
**
** This is the other helper routine for changereg().  It takes the
** same args as creg(), but it merely makes sure the register change
** will not change the given instruction before calling changereg()
** on the instruction before that one.
**
** This is mutually recursive with creg().
*/

static cregbefore (to, from, p, dguard, dstart)
pcode p;
{
    if (to == from) return 1;		/* already right */
    if (from == RETVAL && dropsout (p)) return 0; /* return uses AC1 */
    switch (p->ptype & ADRMODE) {
    case REGIS:				/* careful of dblwords */
	if (p->pop == POP) {		/* only mem change used as REGIS */
	    if (p->pr2 == to) return 0;	/* conflict, lose */
	    else if (p->pr2 == from) {	/* set of reg to change from */
		if (dguard != NULL) return 0; /* wrong reg val */
		p->pr2 = to;		/* make it what we want */
		return 1;		/* win */
	    }
	}
	switch (rchange (p->pop)) {
	case REG_SAME:	    case REG_SET:	case REG_CHANGED:
	case DBL_SET_SGL:   case DBL_CHG_SGL:
	    break;			/* mem is single word, normal case */

	case REG_SET_DBL:   case REG_CHG_DBL:	case DBL_SAME:
	case DBL_SET:	    case DBL_CHANGED: /* can't deal with doublewords */
	    if (p->pr2 != from && p->pr2 != from - 1 &&
		p->pr2 != to && p->pr2 != to - 1) break; /* safe, go on */
	default:			/* else fall through to loserville */
	    return 0;
	}				/* break falls into other reg use */
    case MINDEXED:
    case BYTEPOINT:
	if ((p->pr2 & 017) == to) return 0; /* conflict, lose */
	if ((p->pr2 & 017) == from) {	/* need to change index */
	    if (!creg (to, from, before (p), dguard, dstart)) return 0;
	    p->pr2 ^= (to ^ from);	/* change safely for BYTEPOINT */
	    return 1;			/* and return success */
	}				/* otherwise fall through */
    case RCONST:
    case ONEREG:
    case PFLOAT:			/* no cared-about regs used */
	p = before (p);			/* back up */
	return creg (to, from, p, dguard, dstart);	/* tail-recurse */

    default:
	return 0;
    }
}
/*
** Change register retroactively.
**
** changereg (to, from, p)
**    tries to change the code at and before p in the peephole buffer
**    so that the value that was previously calculated into register from
**    has now been calculated into register to.  The contents of from
**    are not defined after this operation.
**
**    The return value is 1 if the operation was a success, and 0 otherwise.
*/

changereg (to, from, p)
{
    return creg (to, from, p, NULL, previous);
}
/*
** Fold negation into other ops
**
** pushneg (r, p)
**    attempts to negate the value previously calculated into r by the
**    instructions up to p.  No new instructions are added.
**    If the register is marked by the register allocator as in use,
**    we will not touch it, but no checks are made about the use
**    of the register in the instructions following p.
*/

pushneg(r, p)
pcode p;
{
    if (!rfree(r)) return 0;		/* only mung finished regs */
    while (p != NULL) {
	switch (p->pop & (OPCODE | BOTH)) {
	case MOVN:
	case MOVE:
	    if (p->preg != r) break;
	    if (prevskips (p) && !pushneg (r, before (p))) return 0;
	    p->pop ^= (MOVE ^ MOVN);	/* turn MOVE into MOVN, vice versa */
	    return 1;

	case FDVR:	case FMPR:	case IMUL:
	    if (p->preg != r) break;
	    if (!prevskips (p) && p->ptype == REGIS &&
		pushneg (p->pr2, before (p))) return 1;
	    break;			/* neg of either op works */

	case IDIV:
	    if (p->preg + 1 == r) r--;	/* negate dividend for remainder */
	    break;

	case ADD:	case SUB:
	    if (p->preg != r) break;
	    if (!pushneg (r, before (p))) return 0;
	    p->pop ^= (ADD ^ SUB);	/* swap ADD <=> SUB */
	    return 1;			/* return success */

	case SETZ:
	    if (p->preg != r || prevskips (p)) break;
	    return 1;

	case SETO:
	    if (p->preg != r) break;
	    if ((p->ptype & ADRMODE) != ONEREG) return 0;
	    if (prevskips (p) && !pushneg (r, before (p))) return 0;
	    p->pop = MOVE;
	    p->ptype ^= (ONEREG ^ IMMED);
	    p->pvalue = 1;
	    return 1;

	case JUMP:
	    if (p->preg != r) break;
	    if (!pushneg (r, before (p))) return 0;
	    p->pop = swapop (p->pop);
	    return 1;

	case AOJ:   case SOJ:
	    if (p->preg != r) break;
	    if (!pushneg (r, before (p))) return 0;
	    p->pop ^= AOJ ^ SOJ ^ SWPSKIP;
	    return 1;

	case JRST:  case POPJ:
	    break;

	case CAI:
	    if (p->preg != r) break;
	    if ((p->ptype & ADRMODE) != RCONST || !pushneg (r, before (p)))
		return 0;
	    p->pvalue = - p->pvalue;	/* now neg, so negate comparand */
	    p->pop = swapop (p->pop);	/* and comparison */
	    return 1;

	case TRN:   case TDN:	case CAM:   case SKIP:
	case FLTR:  case FADR:	case FSBR:
	    if (p->preg != r) break;
	default:
	    return 0;
	}
	if (p->pindex == r) return 0;	/* can't back over index use */
	p = before (p);			/* back another op */
    }
}
/*
** Undo failed changereg when we don't care which register it is.
**
** We take as argument a register that might have been the destination
** reg of a call to changereg(), and look back for the MOVE R,S that
** would have been emitted if the register couldn't be changed.
** If we find it, we drop it and return S; otherwise we return R.
** Note that R and S are PDP-10 registers, not virtual registers.
*/

int ufcreg (r)
{
    if (previous != NULL && previous->ptype == REGIS /* !prevskips */ &&
	previous->pop == MOVE && previous->preg == r && optimize) {

	r = previous->pr2;		/* remember the new one */
	previous->pop = NOP;		/* flush the now-useless move */
	fixprev();			/* make previous point to something */
    }
    return r;				/* return the register to use */
}