File:  [Research Unix] / researchv9 / cmd / sun / c2 / instruction.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:21:59 2018 UTC (8 years, 1 month ago) by root
Branches: belllabs, MAIN
CVS tags: researchv9-SUN3_old, researchv9-SUN3, HEAD
researchv9-SUN3(old)

#ifndef lint
static	char sccsid[] = "@(#)instruction.c 1.1 86/02/03 Copyr 1985 Sun Micro";
#endif

/*
 * Copyright (c) 1985 by Sun Microsystems, Inc.
 */

#include "as.h"
#include "c2.h"

extern char *malloc();
struct oper *newoperand();
struct oper  operands[OPERANDS_MAX];
int numops;
void new_csect(), save_stabs();

NODE first = { OP_FIRST, SUBOP_Z, &first, &first };

#define nexti( p ) { do p=p->forw; while( ISDIRECTIVE(p->op) ); }
#define previ( p ) { do p=p->back; while( ISDIRECTIVE(p->op) ); }

extern int bytesize[];
#define BYTESIZE( s ) (bytesize[ (int)(s)])

/* does this branch read the c or v bit of the condition code? */
char read_cc_cv[]={ 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0 };
static struct ins_bkt * moveq = NULL;
static err_code operand_error = E_NOERROR;

extern struct ins_bkt *tstw, *tstl;
extern int ext_instruction_set;

static int
numeric_immediate( op )
    struct oper *op;
{
    if (op->type_o != T_IMMED || op->sym_o != NULL )
	return 0;
    return 1;
}

static int
immediate_value( op, value )
    struct oper *op;
    int value;
{
    if (op->type_o != T_IMMED || op->sym_o != NULL || op->flags_o&O_FLOAT || op->value_o != value)
	return 0;
    return 1;
}

instruction( ip )
    struct ins_bkt *ip;
{
    register NODE *np;
    register i;

    if (ip->noper_i != numops && ip->noper_i <= OPERANDS_MAX){
	/* shifts are a special case...*/
	if ((ip->touchop_i&TOUCHMASK) == SPEC(2) || numops==1 || ip->subop_i==SUBOP_W)
	    i = 0;
	else if ( (ip->touchop_i&TOUCH1(TOUCHMASK)) == TOUCH1(SPEC(7)) && numops==0 )
	    /* so is rts */
	    i = 0;
	else
	    prog_error(E_NUMOPS);
    }
    if ( ISINSTRUC(ip->op_i) ){
	if (cur_csect_name != C_TEXT){
	    /* non-text is not delt with -- echo it out directly */
	    putinstr( ip );
	    /* instruction in data space -- punt addressing      */
	    bc = 0x80000000; /* magic, negative number */
	    return;
	}
	switch (numops){
	case 0: i = 1; break;
	case 1: i = operand_ok( ip, &operands[0], (struct oper *)NULL, (struct oper *)NULL  ); break;
	case 2: i = operand_ok( ip, &operands[0], &operands[1], (struct oper *)NULL  );break; 
	case 3: i = operand_ok( ip, &operands[0], &operands[1], &operands[2]  );break; 
	}
	if (!i)
	    prog_warning(operand_error);
    } else {
	switch ( ip->op_i ){ 
	case OP_TEXT:  cur_csect_name = C_TEXT;  new_csect(); return;
	case OP_DATA:  cur_csect_name = C_DATA;  new_csect(); break;
	case OP_DATA1: cur_csect_name = C_DATA1; new_csect(); break;
	case OP_DATA2: cur_csect_name = C_DATA2; new_csect(); break;
	case OP_BSS:   cur_csect_name = C_BSS;   new_csect(); break;
	case OP_ASCII:
	case OP_ASCIZ: bc += ascii(cur_csect_name)+(ip->op_i==OP_ASCIZ); break;
	case OP_STABS: save_stabs(cur_csect_name); break;
	default:       bc += pseudo_size( ip ); break;
	}
	if (cur_csect_name != C_TEXT){
	    /* non-text is not delt with -- echo it out directly */
	    putinstr( ip );
	    return;
	}
    }
    np = new();
    np->nref = numops;
    for (i=0; i<numops; i++){
	np->ref[i] = newoperand( &operands[i] );
    }
    installinstruct( np, ip );
    addnode( np );
}

unsigned short
make_touchop( np, to )
    NODE *np;
    unsigned short to;
{
    /* ripped off from installinstruct, but not yet used by it */
    int i;
    unsigned short tfield, tretval;
    struct oper *o;

    tretval = 0;
    for (i=np->nref-1; i >= 0; i--){
	o = np->ref[i];
	switch(tfield = (to>>(TOUCHWIDTH*i))&TOUCHMASK){
	case SPEC(0): /* bset, bclr, bcng */
		if (o->type_o==T_REG){
		    tfield = touchselect(np->ref[0], BR+BW, WR+WW, LR+LW);
		} else{
		    tfield = (BR+BW);
		}
		break;
	case SPEC(1): /* btst */
		if (o->type_o==T_REG){
		    tfield = touchselect(np->ref[0], BR, WR, LR);
		} else {
		    tfield = (BR);
		}
		break;
	case SPEC(2): /* shifts */
		if (np->nref==1)
		    tfield = (WR+WW);
		else
		    tfield = (WR);
		break;
	case SPEC(3): /* pea, link, unlk    */
		tfield = 0;
		break;
	case SPEC(4): /* bfset  bfins */
		tfield = (LW);
		break;
	case SPEC(5): /* bfext? bftst */
		tfield = (LR);
		break;
	case SPEC(6): /* mulsl, divsll */
		tfield = (LR+LW);
		break;
	}
	tretval <<= TOUCHWIDTH;
	tretval |= tfield;
    }
    return tretval;
}

static int
touchselect( operand, b, w, l )
    struct oper *operand;
{
    register v;
    if (!numeric_immediate( operand ))  return l;
    v = operand->value_o;
    if ( v >=0 && v < 16 )
	if (v < 8 ) return b;
	else return w;
    else return l;
}

static subop_t
subopselect( touchword )
{
    switch (touchword&TOUCHMASK) {
    case BR:
    case BW:
    case BR+BW:
	return SUBOP_B;
    case WR:
    case WW:
    case WR+WW:
	return SUBOP_W;
    }
    return SUBOP_L;
}

void
cannibalize( np, sp )
    NODE *np;
    char *sp;
{
    installinstruct( np, sopcode(sp) );
}

installinstruct( np, ip )
    register NODE *np;
    register struct ins_bkt *ip;
{
    register struct oper *o;
    register unsigned short touchop;
    regmask smask, umask;
    register i;
    static int regnames[] = {
	D0REG, D0REG+1,
	A0REG, A0REG+1,
	FP0REG, FP0REG+1
    };

    np->instr = ip;
    np->op = ip->op_i;
    np->subop = ip->subop_i;
    umask = MAKERMASK( CCREG, (ip->cc_i&CCR)?BR:0);
    smask = MAKEWMASK( CCREG, (ip->cc_i&CCX)?BW:0);
    if (ip->cc_i&FCCR) umask = addmask( umask, MAKERMASK( FPCCREG, BR));
    if (ip->cc_i&FCC) smask = addmask( smask, MAKEWMASK( FPCCREG, BW));
    switch (np->op){
    case OP_CALL: /* writes d0,d1,a0,a1 except with exceptions */
	o = np->ref[0];
	if ( o->type_o == T_NORMAL && o->value_o==0 ){
	    struct sym_bkt *s;
	    s = o->sym_o;
	    if (s->attr_s & S_CRT){
		/*
		 * c- runtime support routine -- some of these may touch
		 * the floating point registers, but as of 3.0 they are
		 * not called by programs that use them.
		 */
		for (i=0; i<4; i++ ){
		    umask = addmask( umask, MAKERMASK( regnames[i], s->builtin_s[i] ));
		    smask = addmask( smask, MAKEWMASK( regnames[i], s->builtin_s[i] ));
		}
		break;
	    }
	}
	/* normal routine -- write d0,d1,a0,a1,fp0,fp1 */
	for (i=0; i<6; i++){
	    smask = addmask(smask,MAKEWMASK( regnames[i], LW ));
	}
	break;
    case OP_EXIT: /* reads some registers, writes all */
	umask  = addmask(exitmask, regmask_nontemp);
	smask = regmask_all;
	break;
    }
    touchop = ip->touchop_i;
    for (i=0; i<np->nref; i++, touchop >>= TOUCHWIDTH ){
	o = np->ref[i];
	switch(touchop&TOUCHMASK){
	case SPEC(0): /* bset, bclr, bcng */
		if (o->type_o==T_REG){
		    touchop = (touchop&~TOUCHMASK)|touchselect(np->ref[0], BR+BW, WR+WW, LR+LW);
		} else{
		    touchop = (touchop&~TOUCHMASK)|(BR+BW);
		}
		np->subop = subopselect( touchop );
		break;
	case SPEC(1): /* btst */
		if (o->type_o==T_REG){
		    touchop = (touchop&~TOUCHMASK)|touchselect(np->ref[0], BR, WR, LR);
		} else {
		    touchop = (touchop&~TOUCHMASK)|(BR);
		}
		np->subop = subopselect( touchop );
		break;
	case SPEC(2): /* shifts */
		if (np->nref==1)
		    touchop = (touchop&~TOUCHMASK)|(WR+WW);
		else
		    touchop = (touchop&~TOUCHMASK)|(WR);
		break;
	case SPEC(3): /* pea, link, unlk    */
		smask = MAKEWMASK( SPREG, LW );
		/* umask = MAKERMASK( SPREG, LR ); */
		/* the unlk instruction does not read sp before setting it */
		umask = (ip->op_i == OP_UNLK) ? regmask0 : smask;
		touchop = LR+LW; /* link and unlk use this */
		break;
	case SPEC(4): /* bfset  bfins */
		touchop = (touchop&~TOUCHMASK)|(LW);
		break;
	case SPEC(5): /* bfext? bftst */
		touchop = (touchop&~TOUCHMASK)|(LR);
		break;
	case SPEC(6): /* mulsl, divsll */
		if (o->type_o != T_REGPAIR){
		    touchop = (touchop&~TOUCHMASK)|(LR+LW);
		    break;
		} else {
		    /* 1st register of pair written only */
		    smask = addmask( smask, MAKEWMASK( o->value_o, LW ));
		    /* 2nd register of pair read+written */
		    umask = addmask( umask, MAKERMASK( o->reg_o,   LR ));
		    smask = addmask( smask, MAKEWMASK( o->reg_o,   LW ));
		    continue;
		}
	}
	switch (o->type_o ){
	case T_REGPAIR:
	    umask = addmask( umask, MAKERMASK( o->reg_o,   touchop ));
	    smask = addmask( smask, MAKEWMASK( o->reg_o,   touchop ));
	    /* FALL THROUGH */
	case T_REG:
	    if (dareg( o->value_o ) || srreg( o->value_o) || freg( o->value_o)){
		umask = addmask( umask, MAKERMASK( o->value_o, touchop ));
		smask = addmask( smask, MAKEWMASK( o->value_o, touchop ));
	    }
	    break;
	case T_POSTINC:
	case T_PREDEC:
	    smask = addmask( smask, MAKEWMASK( o->value_o, LW ));
	    /* FALL THROUGH */
	case T_DEFER:
	    umask = addmask( umask, MAKERMASK( o->value_o, LR ));
	    break;
	case T_DISPL:
	    umask = addmask( umask, MAKERMASK( o->reg_o, LR ));
	    break;
	case T_INDEX:
	    if (! (o->flags_o & O_BSUPRESS))
		umask = addmask( umask, MAKERMASK( o->reg_o, LR ));
	    if ( o->flags_o & (O_PREINDEX|O_POSTINDEX)) 
		umask = addmask( umask, MAKERMASK( o->value_o, 
		    o->flags_o&O_LINDEX ? LR : WR ));
	    break;
	}
	if (o->flags_o & O_BFLD){
	    if (o->flags_o & O_BFOREG)
		umask = addmask( umask, MAKERMASK( o->bfoffset_o, BR ));
	    if (o->flags_o & O_BFWREG)
		umask = addmask( umask, MAKERMASK( o->bfwidth_o, BR ));
	}
    }
    np->ruse = umask;
    np->rset = smask;
}

static unsigned
op_to_bits( op )
    register struct oper *op;
{
    /* return the address-mode bits for operand *op */
    register unsigned s; 
    static unsigned bits[] = {
	0,
	AM_REG, AM_DEFER, AM_POSTINC, AM_PREDEC, AM_DISPL,
	AM_INDEX, AM_ABSS, AM_ABSL, AM_IMMED, AM_NORMAL, 
	AM_REGPAIR, -1, -1
    };
    switch ( s = bits[(int)op->type_o]){
    case AM_REG:
	s = reg_access[op->value_o]; break;
    case AM_DISPL:
	if (op->reg_o == PCREG) s = AM_PCDISPL; break;
    case AM_INDEX:
	if (op->reg_o == PCREG) s = AM_PCINDEX; break;
    }
    return s;
}

int
operand_ok( ip, op1, op2, op3 )
    struct ins_bkt *ip;
    struct oper    *op1, *op2, *op3;
{
    /*
     * this routine answers the eternal question:
     *  are operands *op1 and *op2 ok to use as the operands of instruction *ip?
     * it does it by looking at the optype_i bit fields in the instruction
     * description.
     */
    register i;
    register unsigned opbits1, opbits2, opbits3;
    register noperands = ip->noper_i;

    opbits1 = op_to_bits( op1 );
    if (op2)
	opbits2 = op_to_bits( op2 );
    else
	opbits2 = 0;
    if (op3)
	opbits3 = op_to_bits( op3 );
    else
	opbits3 = 0;
    switch (ip->touchop_i&TOUCH2(TOUCHMASK)){
    case TOUCH2(SPEC(0)):
    case TOUCH2(SPEC(1)):
	/* special hackery for bit ops */
	if (opbits1 == AM_IMMED){
	    i = op1->value_o;
	    if (op1->sym_o) { operand_error =  E_CONSTANT; return 0;}
	    if (opbits2 == AM_DREG){
		if (i < 0 || i > 31 ) { operand_error =  E_CONSTANT; return 0;}
	    } else 
		if (i < 0 || i > 7 ) { operand_error =  E_CONSTANT; return 0;}
	}
	break;
    }
    switch (ip->touchop_i&TOUCH1(TOUCHMASK)){
    case TOUCH1(SPEC(2)):
	/* special hackery for shifts */
	if (opbits1 == AM_IMMED && ((i=op1->value_o)<1 || i>8 || op1->sym_o)){
	    operand_error = E_CONSTANT;
	    return 0;
	}
	break;
    }
    switch (noperands){
    case 0: return 1;
    case 1:
	if ((ip == tstw || ip == tstl) && areg_addr(op1)
	  && !ext_instruction_set)
	    break;
	for (i=0; i<N_OPTYPES; i+=1)
	    if ((opbits1&ip->optype_i[i])==opbits1) return 1;
	break;
    case 2:
	for (i=0; i<N_OPTYPES; i+=2)
	    if (((opbits1&ip->optype_i[i])==opbits1) && ((opbits2&ip->optype_i[i+1])==opbits2) ) return 1;
	break;
    case 3:
	for (i=0; i<N_OPTYPES; i+=3)
	    if (((opbits1&ip->optype_i[i])==opbits1) && ((opbits2&ip->optype_i[i+1])==opbits2) && ((opbits3&ip->optype_i[i+2])==opbits3) ) return 1;
	break;
    }
    operand_error =  E_OPERAND;
    return 0;
}
onceonly()
{
    /*
     * A collection of context-free, compiler specific hacks 
     * we need only do once, as circumstances will not change
     * during the course of our work. These include:
     *  - moveml's: deleting those that do nothing, replacing the degenerate
     *             cases with movl's.
     *  - link a6,#0 ... addl #X,sp: combine if X in range of the link.
     *  - pea 0 : becomes clrl sp@-
     *  - cmpX #0,Y : becomes tstX Y, for Y not an address register.
     *  - movX #0,Y : becomes clrX Y, for Y not a  register, or
     *                        moveq #0,Y, for type long data register, or
     *                        subl Y,Y for Y an address register.
     */
    
    register NODE * n;
    register NODE *f;
    register i, v;
    register struct oper *o;
    int regno;
    extern NODE *deletenode();

    for (n=first.forw; n != &first; n=n->forw){
	switch( n->op){
	case OP_MOVEM:
	    if (n->subop==SUBOP_L || n->subop==SUBOP_X) {
		/* i get the index of the immediate operand */
		i = (n->ref[1]->type_o==T_IMMED); 
		/* o get the pointer to the immediate operand */
		o = n->ref[i];
		if (o->sym_o || o->type_o!=T_IMMED) continue;
		/* v gets the register mask -- how many bits set here? */
		switch( cntbits( v=o->value_o ) ){
		case 0: 
		    if (deladdr(n->ref[1-i])){
			n = deletenode( n ); 
			meter.nrmtfr++;
		    }
		    break;
		case 1: /* replace with a moveml, same "other" operand */
		    v = ffs(v); /* find first bit set -- "1" on littleend */
		    /* if mode of "other" operand is predecrement, bits work backwards */
		    o->type_o=T_REG;
		    if (n->subop==SUBOP_L){
			if (n->ref[1-i]->type_o==T_PREDEC)
			    v = 16 - v;
			o->value_o = v-1;
			cannibalize( n, "movl" );
		    }else{
			o->value_o = FP0REG + 8 - v;
			cannibalize( n, "fmovex" );
		    }
		    meter.nmmtmo++;
		    break;
		}
	    }
	    continue;
	case OP_LINK:
	    /* make sure we understand this instruction */
	    o = n->ref[1];
	    if (!numeric_immediate(o)) continue;
	    v = o->value_o;
	    /* see if next instruction is of form: addl #nnn,sp */
	    f = n;
	    nexti(f);
	    if (f->op!=OP_ADD) continue;
	    o = f->ref[0];
	    if (!numeric_immediate(o)) continue;
	    v += o->value_o;
	    o = f->ref[1];
	    if (!(o->type_o==T_REG && o->value_o==SPREG)) continue; 
	    /* make sure sum of immediates is in range */
	    if (v <-32768 || v > 32767) continue;
	    /* got it! */
	    n->ref[1]->value_o = v;
	    n = deletenode( f );
	    meter.namwl++;
	    continue;
	case OP_PEA:
	    /* if operand is a "normal" 0, change the opcode */
	    o = n->ref[0];
	    if (o->type_o==T_NORMAL && o->sym_o==NULL && o->value_o==0){
		o->type_o = T_PREDEC;
		o->value_o = SPREG;
		cannibalize( n, "clrl" );
		meter.nmtoc++;
	    }
	    continue;
	case OP_CMP:
	    /* compares to zero are degenerate */
	    o = n->ref[0];
	    if (immediate_value( o, 0)){
		if (areg_addr(n->ref[1])){
		    /* "A" register
		     * may be able to do something about this later on,
		     * after we gain some context.
		     */
		    continue;
		} else {
		    n->ref[0] = n->ref[1];
		    n->nref--;
		    freeoperand( o );
		    cannibalize( n,  (n->subop==SUBOP_L)?"tstl"
				    :(n->subop==SUBOP_W)?"tstw"
				    :              "tstb");
		    meter.nctot++;
		}
	    }
	    continue;
	case OP_MOVE:
	    /* moves of zero can often be simplified */
	    o = n->ref[0];
	    if (immediate_value(o, 0)){
		switch (n->ref[1]->type_o){
		case T_REG:
		    v = n->ref[1]->value_o;
		    if (dreg(v) ){
			if (n->subop != SUBOP_L) goto mkclr;
			/* use moveq */
			cannibalize( n, "moveq" );
			meter.nwmov++;
		    } else if (areg(v)){
			/* subtract from self */
			o->type_o = T_REG;
			o->value_o = v;
			cannibalize( n , "subl" );
			meter.nmtos++;
		    }
		    break;
		default:
		mkclr:
		    /* use the clear instruction */
		    n->ref[0] = n->ref[1];
		    n->nref--;
		    freeoperand( o );
		    cannibalize( n,  (n->subop==SUBOP_L)?"clrl"
				    :(n->subop==SUBOP_W)?"clrw"
				    :              "clrb");
		    meter.nmtoc++;
		    break;
		}
	    }
	    continue;
	case OP_AND:
	    /*
	     * if this looks like a mask of the lower bits,
	     * then it kill the rest of the register without
	     * reading it: i.e.
	     *	andl	#255,d0
	     * only uses the low-order 8bits anyway, so we can
	     * change our usage mask.
	     */
	    if (numeric_immediate(o=n->ref[0])){
		v = o->value_o;
		if ((o=n->ref[1])->type_o==T_REG){
		    regno = o->value_o;
		    if ( v>= 0 && v <= 0xffff && dreg(regno) ){
			n->ruse = submask( n->ruse, MAKERMASK( regno, LR ));
			n->ruse = addmask( n->ruse, MAKERMASK( regno, v<=0xff ? BR : WR ));
			n->rlive = compute_normal( n, n->forw->rlive);
		    }
		}
	    }
	    continue;
	}
    }
}

int
addr_delt( old, new )
    subop_t old, new;
{
#define NO_WAY -32768
    /*
     * if an operand was addressed using subop "old", and will be
     * addressed by "new", what is the address increment?
     */
    static char deltsize[7][7] = {
    /* new is ... B    W    L    S    D    X	  P       */
    /* old is ... */
    /* B */ {     0,  -1,  -3,  -3,  -7,  -11,   -11    },
    /* W */ {     1,   0,  -2,  -2,  -6,  -10,   -10    },
    /* L */ {     3,   2,   0,   0,  -4,   -8,    -8    },
    /* S */ {     3,   2,   0,   0,  -4,   -8,    -8    },
    /* D */ {     7,   6,   4,   4,   0,   -4,    -4    },
    /* X */ {    11,  10,   8,   8,   4,    0,     0    }, 
    /* P */ {    11,  10,   8,   8,   4,    0,     0    }, 
    };

    switch ( old ){
    case SUBOP_B:
    case SUBOP_W:
    case SUBOP_L:
	break;
    default: return NO_WAY;
    }
    switch ( new ){
    case SUBOP_B:
    case SUBOP_W:
    case SUBOP_L:
	break;
    default: return NO_WAY;
    }
    return deltsize[(int)old - (int)SUBOP_B][(int)new - (int)SUBOP_B];
#undef NO_WAY
}

int
long_immediate( opcode )
    opcode_t opcode;
{
    /*
     * list some of the opcodes in which immediate operands are long (addl),
     * as opposed to those in which this is not the case (asrl).
     */
    switch ( opcode ){
    case OP_ADD:
    case OP_AND:
    case OP_SUB:
    case OP_MOVE:
    case OP_CMP:
    case OP_OR:
	return 1;
    }
    return 0;
}

static int
exception( p, pnext )
    NODE *p, *pnext;
{	
    if (p->instr==moveq && long_immediate(pnext->op )) 
	return 1; /* don't elide moveq's */
    if (pnext->op == OP_BOP){
	if (!numeric_immediate(pnext->ref[0])) return 1; /* be careful here */
	if (p->subop!=SUBOP_B)
	    if (p->ref[0]->type_o != T_REG && !cancache(p->ref[0]))
		return 1; /* don't mess up memory references */
    }
    return 0;
}

/*
 * have the pattern 
 *   movX   Y,d0
 *   opZ    d0,...
 * where the second instruction only reads the operand, and we could 
 * have done
 *   opZ    Y',...
 * instead.
 */
static NODE *
elide_move( p, pnext, opset )
    register NODE *p;
    register NODE *pnext;
    int opset;
{
    register i;

    for ( i=0 ; i<pnext->nref; i++)
	if (opset & (1<<i)) 
	    *pnext->ref[i] = *p->ref[0];
    installinstruct( pnext, pnext->instr );
    pnext->rlive = compute_normal(pnext,pnext->forw->rlive);
    meter.redunm++;
    return( deletenode( p ));
}

/*
 * look for dbra-equivalent sequence. The compiler
 * already makes some assumptions about 16-bit reachability
 * (word offsets in case-jumps), so we don't worry about that
 * here. We're going to be rather strict about what we recognize,
 * because we don't have the information to determine whether,
 * for instance, an index begins positive, in which case we
 * could recognize the condition " > 0 " as being equivalent
 * to " != -1". Someday, perhaps.
 * We will also be just a little adventuresome and try to
 * handle the "long dbra" case, too. (Long is the data type,
 * not the jump offset!)
 */
static NODE *
make_dbra( p, didchange )
    register NODE *p;
    int *didchange;
{
    register NODE *pnext;
    NODE *p3;
    register struct oper *o;
    subop_t length;
    static struct oper immed = { T_IMMED, 0,0,0, NULL, NULL, NULL, 0 };

    /* recognize subqZ #1,dX */
    length = p->subop;
    if (length == SUBOP_B) return p;
    if ( !immediate_value((o=p->ref[0]), 1))
	return p;
    o=p->ref[1];
    if (!dreg_addr(o)) return p;
    pnext = p->forw;
    switch (pnext->op){
    case OP_CMP:
	/* recognize cmpZ #-1,dX */
	if (pnext->subop != length ||
	    !immediate_value( pnext->ref[0], -1) ||
	    !sameops( o, pnext->ref[1]))
		return p;
	/* recognize jne Y */
	pnext = pnext->forw;
	if (pnext->op != OP_JUMP || pnext->subop != JNE ) return p;
	if (inmask( CCREG, pnext->forw->rlive) || inmask( CCREG, pnext->luse->rlive)) return p;
	/*
	 * found the pattern:
	 *    subqX   #1,dY    <== p
	 *    cmpX    #-1,dY
	 *    jne     Z        <== pnext
	 */
	/* save one copy of the operand register, delete much */
	p->nref --;
	p = deletenode( p)->forw;
	p = deletenode( p)->forw;
	/* p == pnext */
	break;
    case OP_TST:
	/* try a different pattern */
	p3 = p->back;
	if (p3->op != OP_MOVE || p3->subop != length ||
	    !sameops( p3->ref[0], o) || !dreg_addr( p3->ref[1]))
	    return p;
	if (pnext->subop != length || !sameops( p3->ref[1], pnext->ref[0]))
	    return p;
	/* recognize jne Y */
	pnext = pnext->forw;
	if (pnext->op != OP_JUMP || pnext->subop != JNE ) return p;
	if (inmask( CCREG, pnext->forw->rlive) || inmask( CCREG, pnext->luse->rlive)) return p;
	if (inmask( p3->ref[1]->value_o, pnext->rlive )) return p;
	/*
	 * found the pattern:
	 *    movX	dY,dW   <== p3
	 *	  subqX	#1,dY   <== p
	 *    tstX	dW
	 *    jne   Y       <== pnext
	 */
	/* save one copy of the operand register, delete much */
	(void) deletenode( p3 );
	p->nref --;
	p = deletenode( p)->forw;
	p = deletenode( p)->forw;
	/* p == pnext */
	break;
    default:
	return p;
    }
    /* 
     * now: two cases here: simple and long:
     *
     * simple case: move operand o to the jump instruction; 
     * cannibalize it as a dbra;
     *
     * long case: as simple case, then add the long-dbra
     * tail: clrw , cmpl, jcc.
     */
    pnext->ref[0] = o;
    pnext->nref++;
    cannibalize( pnext, "dbra" );
    pnext->op = OP_DJMP;
    if (length == SUBOP_L){
	/* add clrw dX instruction after that */
	pnext = new();
	pnext->nref = 1;
	pnext->ref[0] = newoperand( o );
	insert( pnext, p);
	cannibalize( pnext, "clrw" );
	/* add subql #1 instruction next */
	p3 = pnext;
	pnext = new();
	pnext->nref = 2;
	immed.value_o = 1;
	pnext->ref[0] = newoperand( &immed );
	pnext->ref[1] = newoperand( o );
	insert( pnext, p3);
	cannibalize( pnext, "subql" );
	/* finally, add jcc Y instruction */
	p3 = pnext;
	pnext = new();
	insert( pnext, p3);
	newreference( p->luse, pnext );
	cannibalize( pnext, "jcc" );
	pnext->op = OP_JUMP;
    }
    meter.ndbra++;
    (*didchange)++;
    return p;
}

/*
 * find out if operand *o uses the register r 
 */
int
operand_uses( o, r )
    struct oper *o;
    int r;
{
    int t;
    switch (o->type_o){
    case T_REG:
    case T_DEFER:
    case T_POSTINC:
    case T_PREDEC:
	return (o->value_o == r);
    case T_DISPL:
	return (o->reg_o == r);
    case T_INDEX:
	t  = (o->flags_o & O_BSUPRESS)?0 : (o->reg_o == r);
	t |= (o->flags_o & (O_PREINDEX|O_POSTINDEX))? (o->value_o == r) : 0;
	return t;
    case T_REGPAIR:
	return (o->value_o == r || o->reg_o == r);
    }
    return 0; /* others don't use registers */
}

/*
 * Look for arithmetic on an A register. Look for nearby uses of
 * the same register using deferred addressing. See if we can combine
 * them into postincrement or predecrement mode addressing.
 */
NODE *
plus_minus( p, didchange )
    register NODE *p;
    int *didchange;
{
    int incrval;
    register int reg;
    register NODE *q;
    int found;
    int i;
    operand_t substitute;
    register struct oper *o;

    if ( numeric_immediate( p->ref[0] ) && areg_addr( p->ref[1] ) ){
	incrval = p->ref[0]->value_o;
	reg = p->ref[1]->value_o;
	q = p;
	found = 0;
	if ( p->op == OP_ADD ){
	    /* search backwards */
	    substitute = T_POSTINC;
	    while ( (q=q->back)->op != OP_FIRST && q->op != OP_LABEL
	    && !ISBRANCH(q->op) && ! inmask( reg, q->rset)){
		if (inmask( reg, q->ruse)){ 
		    found = 1; 
		    break;
		}
	    }
	} else {
	    /* search forwards */
	    substitute = T_PREDEC;
	    while ( (q=q->forw)->op != OP_FIRST && q->op != OP_LABEL
	    && !ISBRANCH(q->op) && ! inmask( reg, q->rset)){
		if (inmask( reg, q->ruse)){ 
		    found = 1; 
		    break;
		}
	    }
	}
	if (found){
	    /*
	     * we've found a nearby node that reads our register.
	     * it must now meet these criteria:
	     *  -- the size of the access must agree with the size of the
	     *     increment/decrement operation
	     * 	-- must not change the register (already checked)
	     *  -- must use the register exactly once
	     *  -- must use the register in a type T_DEFER operand
	     *  -- must be able to take the substituted operand type
	     */
	    switch (q->subop){
	    case SUBOP_B:  found = incrval==1; break;
	    case SUBOP_W:  found = incrval==2; break;
	    case SUBOP_L:  found = incrval==4; break;
	    case SUBOP_S:  found = incrval==4; break;
	    case SUBOP_D:  found = incrval==8; break;
	    case SUBOP_X:  found = incrval==12; break;
	    default: 	   found = 0; break;
	    }
	    if (found){
		found = 0;
		for (i=0; i < q->nref; i++)
		    if (operand_uses( q->ref[i], reg)){
			found++;
			o = q->ref[i];
		    }
		if (found == 1 && o->type_o == T_DEFER){
		    o->type_o = substitute;
		    if (operand_ok(q->instr, q->ref[0], q->ref[1], q->ref[2] )){
			/* got it */
			p = deletenode( p );
			q->rset = addmask( q->rset, MAKEWMASK( reg, LW ) );
			*didchange++;
			meter.nplusminus++;
		    } else {
			/* blew it */
			o->type_o = T_DEFER; /* restore operand, go home */
		    }
		}
	    }
	}
    }
    return p;
}

int
shorten()
{
    int nchanged = 0;
    register NODE *p, *pnext;
    NODE *pprev, *p3;
    register struct oper *o;
    register regno, t;
    int didchange;
    char *newname;
    int i, newcc, opset;
    int otherreg;
    regmask m;
    struct oper *trialops[OPERANDS_MAX];

    /*
     * delete instructions that do useless things:
     *	moves to registers or parts of registers that are dead;
     *  extends to parts of registers that are dead;
     *  reset the condition codes to its current value.
     * also, anything that depends on context which might
     *  be changed by rearranging branchs:
     *  resetting condition codes, multiple shifts in a row,
     *  changing a "cmpl #0,A0" into a "movl A0,Di", where Di is dead.
     *  and the transformation
     *  - subqw #1,dX; cmpw #-1,dX; jne Y: becomes dbra dX,Y
     *			     (most often requested hack).
     */ 
    if (moveq==NULL) moveq = sopcode( "moveq" ); 
    didchange = 1; 
    while(didchange){ 
	didchange = 0;
	for (p=first.forw; p != &first; p=p->forw){
	    if (!ISINSTRUC(p->op)) continue;
	    if (!emptymask( submask(p->rset , addmask( p->forw->rlive, MAKERMASK( CCREG, LR)))) ){
		switch (p->op){
		case OP_CALL:
		case OP_DBRA:
		case OP_DJMP:
		case OP_BRANCH:
		case OP_JUMP:
		    break;
		case OP_EXT:
		    /* ext's also set condition codes */
		    if (!inmask( CCREG, p->forw->rlive)){
			/* useless extend -- simply delete it */
			p = deletenode( p );
			didchange++;
			meter.nredext++;
		    }
		    break;
		case OP_MOVE:
		    /* be careful here -- useless set must be to a register */
		    /* for instance: movl #0,a0@+ sets a0, but we cannot change it */
		    o = p->ref[1]; /* object of move */
		    if (o->type_o != T_REG) break;
		    if (!datareg(regno = o->value_o)) break;
		    /* TEST FOR CC USAGE */
		    if ( dreg(regno) && inmask( CCREG, p->forw->rlive)) break;
		    if ( freg(regno) && inmask( FPCCREG, p->forw->rlive)) break;
		    switch (inmask(regno,  p->forw->rlive)) {
		    case 0:
			if (!deladdr( p->ref[0])) continue; /* side effects? */
			/* totally useless -- fugg it */
			p = deletenode( p );
			didchange++;
			meter.redunm++;
			continue;
		    case 1: /* BYTE ONLY */
			newname = "movb";
			t = (p->subop ==SUBOP_L )?3 : 1; /* could have been movw */
			break;
		    case 2:
		    case 3: /* WORD ONLY */
			newname = "movw";
			t = 2;
			break;
		    default: continue;
		    } 
		    if ( freg(regno) ) break; /* don't rewrite fp moves */
		    if (incraddr( p->ref[0], t)) break;/* not ok */
		    if (p->instr == moveq) break; /* already moveq */
		    cannibalize( p, newname );
		    p->rlive = compute_normal( p, p->forw->rlive );
		    didchange++;
		    meter.nwmov++;
		    continue;
		case OP_AND:
		    /*
		     * if we're doing an AND to a dead register, it must be
		     * for condition-code only (don't even bother to check).
		     * if its only a single bit, change this to a btst.
		     */
		    if ((o=p->ref[1])->type_o!=T_REG || !dreg(regno=o->value_o))
			break;
		    if (inmask(regno, p->forw->rlive)!=0)
			break;
		    if (!numeric_immediate(o=p->ref[0]))
			break;
		    if (cntbits( t = o->value_o ) == 1){
			o->value_o = ffs(t)-1;
			cannibalize( p, "btst" );
			p->rlive = compute_normal( p, p->forw->rlive );
			didchange++;
			meter.nwop++;
		    }
		    break;
		}
	    }
	    switch (p->op){
	    case OP_MOVE:
		/*
		 * If this is a move to a register which is only read once
		 * by a following instruction, see if we can elide a move.
		 * If this is a move to a register which is changed once
		 * in the next instruction, then written back whence it
		 * came, try doing the modify directly to the source 
		 * of it all.
		 * If this is a move of a register to itself, and condition
		 * codes are dead, elide the move.
		 * SPECIAL HACK: the sequence
		 *	moveq	#n, d0
		 *	movl	d0, XXX
		 * is FASTER and shorter than the instruction
		 *	movl	#n, XXX
		 * DO NOT UNDO THIS OPTIMIZATION!
		 */
		if ((o=p->ref[1])->type_o != T_REG) break;
		regno = o->value_o;
		if ( p->ref[0]->type_o ==T_REG && p->ref[0]->value_o == regno){
		    /* either:
		     * its a Dreg or Freg, and the condition code is useless,
		     * or its an Areg, and we're not doing some funny
		     * sign extension.
		     */
		    if ( ((dreg(regno)||freg(regno))  && !inmask( CCREG, p->forw->rlive))
		    || (areg(regno) && p->subop == SUBOP_L)){
			p = deletenode( p );
			didchange++;
			meter.redunm++;
			break;
		    }
		}
		newcc = 0;
		p3 = p;
		nexti(p3);
		for (pnext=p3; ISINSTRUC(pnext->op)||ISDIRECTIVE(pnext->op); pnext=pnext->forw){
		    if (ISDIRECTIVE(pnext->op)) continue;
		    newcc = inmask( CCREG, pnext->rset );
		    if (inmask( regno, pnext->ruse)) break;
		    if ((!newcc && inmask(CCREG, pnext->ruse )) || inmask( regno,  pnext->rset))
			goto no_cookie;
		    if (!emptymask( andmask( pnext->rset,p->ruse)) || !emptymask( andmask( p->rset,pnext->ruse)) )
			goto no_cookie;
		    switch (pnext->op){
		    case OP_BRANCH:
		    case OP_DBRA:
		    case OP_JUMP:
		    case OP_DJMP:
			goto no_cookie;
		    }
		}
		if (!ISINSTRUC(pnext->op)) break;
		/* see if operands the same */
		/* for each operand #i that is the same, opset gets bit 1<<i set */
		opset = 0;
		for ( i=0 ; i<pnext->nref; i++)
		    if (sameops( o, pnext->ref[i])) opset |= 1<<i;
		if (!opset) break;
		/*
		 * We have only two cases we are smart enough to understand.
		 * Either the two instructions are ajacent, in which case
		 * there can have been no side effects in the intervening
		 * (null) instructions, OR we're looking at this pattern:
		 *	movX Y,d0; ... ; opX d0,dZ
		 * where dZ is not used between the moves. The reason for
		 * this restriction is that it's too easy to break subtile
		 * C constructs like post-incrementing, and too hard to detect
		 * the general case.
		 */
		if (pnext==p3){
		    /*
		     * see if register used after that instruction
		     * make sure its a read-only access, and same size
		     */
		    if (!inmask(regno, pnext->forw->rlive) && !inmask( regno, pnext->rset ) ){
			t = addr_delt(p->subop, pnext->subop);
			for ( i=0 ; i<pnext->nref; i++)
			    trialops[i] = (opset & (1<<i) ) ? p->ref[0] : pnext->ref[i];
			for ( i ; i<OPERANDS_MAX; i++)
				trialops[i]  = NULL;
			if (operand_ok(pnext->instr, trialops[0], trialops[1], trialops[2])){
			    int ok = 0;
			    if (t==0) {
				/* both instructions use same size data */
				ok = 1;
			    } else if (t>0 && !areg(regno)) {
				/*
				 * second inst uses smaller data, and
				 * doesn't depend on sign-extension. The
				 * check on regno prevents us from changing
				 *	movl a6@(8),a0; cmpw #0,a0 | RIGHT
				 * into
				 *	cmpw #0,a6@(10)		   | WRONG
				 */
				ok = !incraddr(p->ref[0],t);
			    }
			    if (ok && !exception( p, pnext)){
				p = elide_move(p, pnext, opset);
				didchange++;
				break;
			    }
			} else if (pnext->op == OP_BOP && 
			numeric_immediate( pnext->ref[0] )){
			    /*
			     * btst  #t,d0
			     * take t modulo 8
			     */
			    t = pnext->ref[0]->value_o;
			    pnext->ref[0]->value_o = t%8;
			    if (operand_ok(pnext->instr, trialops[0], trialops[1], trialops[2]) &&
			    !incraddr( p->ref[0], BYTESIZE(p->subop)-1-t/8 )){
				/* it worked */
				p = elide_move(p, pnext, opset);
				didchange++;
				break;
			    } else {
				/* didn't work; put it back */
				pnext->ref[0]->value_o = t;
			    }
				
			}
		    }
		}else{
		    if (pnext->nref!=2 || !dareg_addr(pnext->ref[1])) break;
		    otherreg =  pnext->ref[1]->value_o;
		    for (p3=p->forw; p3!=pnext; p3=p3->forw){
			if (inmask( otherreg, addmask(p3->ruse,p3->rset) )) break;
		    }
		    if (p3!=pnext ) break;
		    if (!inmask( regno, pnext->forw->rlive ) && !inmask( CCREG, pnext->forw->rlive)
		    && ! (inmask( regno, pnext->rset) || p->subop != pnext->subop )){
			if ( operand_ok(pnext->instr,p->ref[0],pnext->ref[1], 0 )){
			    freeoperand( p->ref[1] );
			    p->ref[1] = pnext->ref[1];
			    installinstruct( p, pnext->instr );
			    pnext->nref=1;
			    m = pnext->forw->rlive;
			    for (p3=pnext->back; p3!=p; p3=p3->back)
				p3->rlive = m = compute_normal( p3, m);
			    p->rlive = compute_normal(p, m );
			    (void) deletenode(pnext);
			    didchange++;
			    meter.redunm++;
			}
		    }
		    break;
		}

		/* see if the operand is written in the next instruction,
		 * then moved back in the following, and dead thereafter 
		 */
    no_cookie:
		if (inmask( regno, pnext->rset ) && (p3=pnext->forw)->op==OP_MOVE 
		&& sameops( o, p3->ref[0] ) 
		&& (pnext->nref== 1 || !sameops( o, pnext->ref[0] ) )
		&& sameops( p->ref[0], p3->ref[1] ) && !inmask(regno , p3->forw->rlive)
		&& p->subop==pnext->subop && pnext->subop==p3->subop )
		{
		    if (pnext->nref==1 && operand_ok( pnext->instr, p->ref[0], NULL, 0 )){
			/* looks like a negX d0 or notX d0 */
			/* move p's operand to pnext */
			freeoperand( pnext->ref[0] );
			pnext->ref[0] = p->ref[0];
			p->ref[0] = NULL;
			/* delete p and p3 */
			p = deletenode( p ); 
			(void )deletenode( p3 );
			/* recompute register info */
			installinstruct( pnext, pnext->instr );
			pnext->rlive = compute_normal(pnext, pnext->forw->rlive );
			didchange++;
			meter.redunm += 2;
			break;
		    } else if (pnext->nref==2 && operand_ok( pnext->instr, pnext->ref[0], p->ref[0], 0 )){
			/* looks like a addX #Y,d0 */
			/* move p's operand to pnext */
			freeoperand( pnext->ref[1] );
			pnext->ref[1] = p->ref[0];
			p->ref[0] = NULL;
			/* delete p and p3 */
			p = deletenode( p ); 
			(void )deletenode( p3 );
			/* recompute register info */
			installinstruct( pnext, pnext->instr );
			pnext->rlive = compute_normal(pnext, pnext->forw->rlive );
			didchange++;
			meter.redunm += 2;
			break;
		    } else if (pnext->nref==2 && !inmask(regno , p3->forw->rlive) 
		    && operand_ok( pnext->instr, o, p->ref[0], 0 )){
			/* 
			 * change:
			 *	movX	A,d0    <== p
			 *	opX	B,d0    <== pnext
			 *	movX	d0,A    <== p3
			 * into:
			 *	movX	B,d0    <== p
			 *	opX	d0,A    <== pnext
			 */
			p->ref[0] = pnext->ref[0];
			installinstruct( p, p->instr );
			pnext->ref[0] = pnext->ref[1];
			pnext->ref[1] = p3->ref[1];
			p3->nref=1;
			(void)deletenode( p3 );
			installinstruct( pnext, pnext->instr );
			pnext->rlive = m = compute_normal( pnext, pnext->forw->rlive);
			p->rlive = compute_normal( p, m );
			didchange++;
			meter.redunm ++;
			break;
		    }
		}
		break;
		    
	    case OP_TST:
		/*
		 * if a previous instruction touched this as an operand
		 * AND set cc on it in a way we appreciate, then delete this
		 * EXTRA HACKERY:
		 *    some instructions (e.g.: lsrl) set the condition codes
		 *    in a way that we can use MOST of the time. others
		 *    (esp moves) set the cond codes in a way that is ok
		 *    all of the time. If the previous instruction is not
		 *    a move, we will try to find the instruction checking
		 *    the cc, and decide if this is an extraordinary case.
		 */
		pprev = p;
		do{
		    previ( pprev );
		} while ( !ISBRANCH(pprev->op) && !(pprev->op==OP_LABEL) && 
		    !inmask(CCREG, p->rset));
		if (!ISINSTRUC(pprev->op) || pprev->nref<1 || pprev->subop != p->subop) break;
		/* integer cc is very funny -- floating cc very regular */
		if (pprev->op!=OP_MOVE){
		    for( p3 = p->forw;  p3->op != OP_FIRST; ){
			switch (p3->op){
			case OP_LABEL:
			    nexti(p3);
			    continue;
			case OP_JUMP:
			case OP_BRANCH:
			case OP_DBRA:
			case OP_DJMP:
			    break;
			default:
			    if (inmask( CCREG, p3->ruse))
				break;
			    nexti(p3);
			    continue;
			}
			break;
		    }
		    if (!inmask( CCREG, p3->ruse)) break;/* cannot find user */
		    if (p3->op == OP_BRANCH || p3->op == OP_DBRA) 
			break;  /* too hard */
		    if (p3->op == OP_JUMP || p3->op == OP_DJMP){
			if (inmask( CCREG, p3->luse->rlive)) break; /* double use */
			if (p3->subop != JALL)
			    if (inmask( CCREG, p3->forw->rlive)) break; /* double use */
		    }
		    if (read_cc_cv[(int)p3->subop - (int)JEQ]) break; /* untrustworthy */
		}
		o = p->ref[0];
		if (!deladdr( o )) break; /* not ok to remove this reference */
		/*
		 * if we set condition codes on this, AND its not an
		 * Areg (on which condition codes are never set, then
		 * its ok to delete the test
		 */
		if (pprev->nref>=1 && sameops( pprev->ref[0], o)){
		    if (((pprev->instr->cc_i&CCX) == CC1) || 
		    ( pprev->op == OP_MOVE  && !areg_addr( pprev->ref[1])) ){
			p = deletenode( p );
			didchange++;
			meter.nrtst++;
		    }
		} else if (pprev->nref==2 && sameops( pprev->ref[1], o)){
		    if (((pprev->instr->cc_i&CCX) == CC2) && !areg_addr(o) ){
			p = deletenode( p );
			didchange++;
			meter.nrtst++;
		    }
		}
		break;
	    case OP_FTST:
		/*
		 * Floating-point instructions always set their condition codes
		 * on the result. The exception is that a move from the 
		 * coprocessor does not set the condition codes at all.
		 */
		/* see if we can find an instruction that sets fpcc */
		pprev = p;
		do{
		    previ( pprev );
		} while ( !ISBRANCH(pprev->op) && !(pprev->op==OP_LABEL) && 
		    !inmask(FPCCREG, p->rset));
		if (!ISINSTRUC(pprev->op) || pprev->nref<1 || pprev->subop != p->subop) break;
		o = p->ref[0];
		if ((pprev->op ==OP_MOVE && sameops( o, pprev->ref[0] ))||
		    (pprev->op !=OP_MOVE && sameops( o, pprev->ref[pprev->nref-1] ))){
		    p = deletenode( p );
		    didchange++;
		    meter.nrtst++;
		}
		break;
	    case OP_OR:
		/*
		 * if what we're or-ing will fit into a byte or word, then
		 * there's no excuse for using a longer immediate.
		 * unless condition codes are live.
		 */
		if ( inmask( CCREG, p->forw->rlive)) break;
		if (numeric_immediate(o=p->ref[0])){
		    t = o->value_o;
		    if (t==0 && deladdr(p->ref[1] )){
			p = deletenode(p);
			didchange++;
			break;
		    }
		    if (t>0 && t<0xffff){
			i = (p->subop==SUBOP_L)?2:0;
			newname = "orw";
			if (t<0xff){
			    i = (p->subop==SUBOP_L)?3:(p->subop==SUBOP_W)?1:0;
			    newname = "orb";
			}
			if (i && !incraddr( p->ref[1], i)){
			    cannibalize( p, newname);
			    meter.nwop++;
			    didchange++;
			}
		    }
			
		}
		break;
	    case OP_ASL:
		/* look for multiple shifts in a row */
		if (!numeric_immediate(o=p->ref[0])) continue;
		t = o->value_o;
		o = p->ref[1];
		while ((pnext=p->forw)->op==OP_ASL){
		    register struct oper *iop;
		    if (!numeric_immediate(iop=pnext->ref[0])
		    || !sameops( pnext->ref[1], o))
			break;
		    if ( t + iop->value_o > 8){
			p->ref[0]->value_o = t;
			t = iop->value_o;
			p = pnext;
			o = p->ref[1];
			continue;
		    }
		    t += iop->value_o;
		    (void)deletenode( pnext );
		    meter.nredshf++;
		    didchange++;
		}
		p->ref[0]->value_o = t;
		break;
	    case OP_CMP:
		/* compares to zero are degenerate */
		o = p->ref[0];
		if (immediate_value(o, 0) && areg_addr(p->ref[1])){
		    /* "A" register */
		    /* look for a dead D register. Make this a movl */
		    if ((i=dead_dreg(p)) >= 0){
			/* change cmpx #0,ay => movl ay,di */
			/* reuse the #0 operand as the di operand */
			p->ref[0] = p->ref[1];
			o->type_o = T_REG;
			o->value_o = i;
			p->ref[1] = o;
			cannibalize( p, "movl" );
			p->rlive = compute_normal( p, p->forw->rlive );
			meter.nttomo++;
			didchange++;
		    }
		}
		break;
	    case OP_LEA:
		/*
		 * this form seems to occur rather often:
		 *	lea	XXX,a0
		 *	movl	a0,aN
		 * with a0 dead beyond this point. Change this into
		 *	lea	XXX,aN
		 * then, look for the case
		 *	lea	aN@(0,dM:Z),aN
		 * and simplify it into
		 *	addZ	dM,aN
		 */
		if ((pnext=p->forw)->op == OP_MOVE
		  && pnext->subop == SUBOP_L 
		  && sameops( p->ref[1], pnext->ref[0])
		  && emptymask( andmask(p->rset,pnext->forw->rlive))) {
		    /* if the move is to an A register, we're set */
		    o=pnext->ref[1];
		    if (areg_addr(o)){
			pnext->ref[1] = p->ref[1]; /* give away our 2nd operand */
			p->ref[1] = o; /* gain a new operand */
			(void) deletenode( pnext ); /* get rid of next, with operands */
			p->rset = MAKEWMASK( o->value_o, LW );
			p->rlive = compute_normal( p, p->forw->rlive);
			didchange++;
			meter.redunm++;
		    }
		}
		if ((o=p->ref[0])->type_o == T_INDEX
		  && o->reg_o == p->ref[1]->value_o
		  && o->scale_o == 1
		  && !(o->flags_o&(O_BSUPRESS|O_INDIRECT|O_POSTINDEX))
		  && o->disp_o == 0){
		    o->type_o = T_REG;
		    newname = (o->flags_o&O_WINDEX) ? "addw" : "addl";
		    o->flags_o &= ~(O_WINDEX|O_LINDEX);
		    cannibalize( p, newname );
		    meter.nsaddr++;
		    didchange++;
		}
		break;

	    case OP_SUB:
		p = make_dbra( p, &didchange );
		if (p->op != OP_SUB) break;
		/* FALL THROUGH */
	    case OP_ADD:
		p = plus_minus( p, &didchange );
		break;
	    }
	}
	nchanged += didchange;
    }
    return nchanged;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.