File:  [Research Unix] / researchv9 / cmd / sun / c2 / stackops.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[] = "@(#)stackops.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 struct oper *newoperand();

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

#define ispushop(o) ((o)->type_o == T_PREDEC && (o)->value_o == SPREG)
#define ispopop(o) ((o)->type_o == T_POSTINC && (o)->value_o == SPREG)

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

/*
 * replace
 * 	(b)->move X,sp@-
 *	     ...
 *	(n)->move sp@+,Rn
 * with
 *	(b)->move X,Rn
 *	     ...
 *	(n) deleted
 * Note that if Rn is live after (n), it now becomes live
 * at all of the points in between (b) and (n).
 */

static NODE *
movedef(b,n,ip)
    register NODE *b;
    NODE *n;
    struct ins_bkt *ip;
{
    register NODE *p;
    regmask l;

    if (b->nref == 2)
	freeoperand(b->ref[1]);
    else 
	b->nref = 2;
    b->ref[1] = newoperand(n->ref[1]);
    installinstruct(b,ip);
    n = deletenode(n);
    l = n->forw->rlive;
    for (p = n; p != b; p = p->back) {
	l = p->rlive = compute_normal(p, l);
    }
    return n;
}

/*
 * replace
 * 	(b)->pea X
 *	     ...
 *	(n)->move sp@+,dn
 * with
 *	(b)->lea X,ar
 *	     movl ar,dn
 *	     ...
 *	(n)  (deleted)
 *
 * This is like the previous transformation with an
 * adjustment for the fact that 'lea' does not work
 * with a d-register destination.
 */
static NODE*
movedef2(b, n, ar, ip)
    register NODE *b;
    register NODE *n;
    int ar;
    struct ins_bkt *ip;
{
    register NODE *p;
    register struct oper *o;
    regmask l;

    /*
     * rewrite (b) as "lea X,ar"
     */
    b->nref = 2;
    o = newoperand(n->ref[1]);
    o->value_o = ar;
    b->ref[1] = o;
    installinstruct(b,ip);
    /*
     * insert "movl ar,dn" following (b)
     */
    p = new();
    p->nref = 2;
    o = newoperand(o);
    o->value_o = ar;
    p->ref[0] = o;
    o = newoperand(o);
    o->value_o = n->ref[1]->value_o;
    p->ref[1] = o;
    cannibalize(p, "movl");
    insert(p,b);
    /*
     * delete (n), update register lifetime information
     */
    n = deletenode(n);
    l = n->forw->rlive;
    for (p = n; p != b; p = p->back) {
	l = p->rlive = compute_normal(p, l);
    }
    return n;
}


/*
 * replace
 * 	(b)->move X,sp@-
 *	     ...
 *	(n)-><op> sp@+,...
 * with
 *	(b) (deleted)
 *	     ...
 *	(n)-><op> X,...
 * Note that if X is a register, it now becomes live
 * at all of the points in between (b) and (n).
 */
static NODE *
moveuse(b, n, srce, ip)
    NODE *b,*n;
    struct oper *srce;
    struct ins_bkt *ip;
{
    register NODE *p;
    regmask l;

    freeoperand(n->ref[0]);
    n->ref[0] = srce;
    b = deletenode(b);
    installinstruct(n,ip);
    l = n->forw->rlive;
    for (p = n; p != b; p = p->back) {
	l = p->rlive = compute_normal(p, l);
    }
    return(n);
}


/*
 * returns the "extended mode" instruction, if any,
 * corresponding to the the instruction described by (ip).
 */
struct ins_bkt *
extendedop(ip)
    struct ins_bkt *ip;
{
    char buf[64];

    (void)strcpy(buf, ip->text_i);
    buf[strlen(buf)-1] = 'x';
    return(sopcode(buf));
}

/*
 * returns 1 if operands (op1) and (op2) may overlap
 */
int
mayoverlap(op1, op2, subop1, subop2)
    register struct oper *op1;
    register struct oper *op2;
    subop_t subop1;
    subop_t subop2;
{
    int r1, r2;
    struct sym_bkt *sym1, *sym2;
    int lb1, lb2, ub1, ub2;	/* byte offset bounds of op1 and op2 */
    int glb, lub;		/* greatest lower & least upper bounds */

    r1 = -1;
    r2 = -1;
    lb1 = 0;
    lb2 = 0;
    sym1 = NULL;
    sym2 = NULL;
    switch(op1->type_o) {
    case T_IMMED:
	return(0);
    case T_REG:
	return(op2->type_o == T_REG && op1->value_o == op2->value_o);
    case T_POSTINC:
    case T_PREDEC:
	/* assume sp@+ and sp@- only overlap with each other */
	if (op1->value_o == SPREG) {
	    if (op2->type_o == T_POSTINC || op2->type_o == T_PREDEC)
		return(op2->value_o == SPREG);
	    return(0);
	}
	return(1);
    case T_DISPL:
	r1 = op1->reg_o;
	/* fall through */
    case T_ABSS:
    case T_ABSL:
    case T_NORMAL:
	sym1 = op1->sym_o;
	lb1 = op1->value_o;
	ub1 = lb1 + BYTESIZE(subop1)-1;
	break;
    case T_DEFER:
	r1 = op1->value_o;
	ub1 = lb1 + BYTESIZE(subop1)-1;
	break;
    }
    switch(op2->type_o) {
    case T_IMMED:
    case T_REG:
	return(0);
    case T_POSTINC:
    case T_PREDEC:
	return(op2->value_o != SPREG);
    case T_DISPL:
	r2 = op2->reg_o;
	/* fall through */
    case T_ABSS:
    case T_ABSL:
    case T_NORMAL:
	sym2 = op2->sym_o;
	lb2 = op2->value_o;
	ub2 = lb2 + BYTESIZE(subop2)-1;
	goto compute_overlap;
    case T_DEFER:
	r2 = op2->value_o;
	ub2 = lb2 + BYTESIZE(subop2)-1;
	/* fall through */
    compute_overlap:
	if (r1 != r2 || sym1 != sym2)
	    return(1);
	ub1 = lb1 + BYTESIZE(subop1)-1;
	ub2 = lb2 + BYTESIZE(subop2)-1;
	glb = lb1 > lb2 ? lb1 : lb2;
	lub = ub1 < ub2 ? ub1 : ub2;
	return(glb <= lub);
    default:
	return(1);
    }
}

/*
 * returns 1 if two operands are in adjacent *addressable* words
 */
int
adjacent(op1, op2)
    register struct oper *op1,*op2;
{
    int w;
    struct oper temp;

    if (op1->type_o != op2->type_o) {
	/* make DEFER operands look like DISPL operands */
	if (op1->type_o == T_DEFER) {
	    temp.type_o = T_DISPL;
	    temp.reg_o = op1->value_o;
	    temp.value_o = 0;
	    op1 = &temp;
	} else if (op2->type_o == T_DEFER) {
	    temp.type_o = T_DISPL;
	    temp.reg_o = op2->value_o;
	    temp.value_o = 0;
	    op2 = &temp;
	} else {
	    return 0;
	}
    }
    switch (op1->type_o){
    case T_IMMED:
    case T_NORMAL:
    case T_ABSS:
    case T_ABSL:
	if (op1->sym_o != op2->sym_o) return 0; /* oops -- complex*/
	/* fall through */
    case T_REG: 
    case T_DEFER:
    case T_POSTINC:
    case T_PREDEC:
	if (op1->value_o + sizeof(long) != op2->value_o) return 0;
	break;
    case T_INDEX:
	if (op1->disp_o + sizeof(long) != op2->disp_o) return 0;
	/* fall through */
    case T_DISPL:
	if (op1->reg_o != op2->reg_o) return 0;
	if (op1->value_o + sizeof(long) != op2->value_o) return 0;
	break;
    default:
	return 0;
    }
    return 1;
}

/*
 * returns 1 if node (n) may use/modify operand (o)
 */
int
may_touch(n, o, osubop, touchmask)
    register NODE *n;
    register struct oper *o;
    int osubop;	/* subop of instruction using (o) */
    register touchmask;
{
    register int i,v;
    regmask l;

    switch(o->type_o) {
    case T_IMMED:
	return 0;
    case T_REG:
	if (touchmask&RMASK == touchmask)
	    return inmask(o->value_o,n->ruse);
	if (touchmask&WMASK == touchmask)
	    return inmask(o->value_o,n->rset);
	if (touchmask)
	    return inmask(o->value_o,addmask(n->rset,n->ruse));
	return 0;
    default:
	/*
	 * for each operand modified by (n), see if
	 * a potential overlap with (o) exists.
	 */
	if (n->op == OP_CALL)
	    return 1;
	v = make_touchop(n, n->instr->touchop_i);
	for (i = 0; i < n->nref; i++) {
	    if (v&touchmask) {
		if (mayoverlap(o, n->ref[i], osubop, n->subop))
		    return 1;
	    }
	    v >>= TOUCHWIDTH;
	}
    }
    return 0;
}


/*
 * reduce stack traffic exposed by inline expansion
 * of procedures
 */
int
stackops()
{
    register NODE *n;
    register NODE *p,*b;
    register struct ins_bkt *ip;
    register struct oper *o;
    register int regno;
    extern NODE *deletenode();
    int changes;
    regmask l;
    int offset;
    int r;

    changes = 0;
    for (n=first.forw; n != &first; n=n->forw) {

	/*
	 * Pattern #0:
	 *	(0) sub  #X,sp
	 *	(1) add  #Y,sp	-or- (1) lea sp@(Y),sp
	 * Rewrite:
	 *	delete both, or use a single addql, subql, or lea
	 */
	if (n->op == OP_SUB
	  && n->ref[1]->type_o == T_REG && n->ref[1]->value_o == SPREG
	  && n->ref[0]->type_o == T_IMMED) {
	    p = n->forw;
	    switch(p->op) {
	    case OP_ADD:
		o = p->ref[1];
		if (o->type_o == T_REG && o->value_o == SPREG
		  && p->ref[0]->type_o == T_IMMED) {
		    goto delete_adjust;
		}
		break;
	    case OP_LEA:
		o = p->ref[1];
		if (o->type_o == T_REG && o->value_o == SPREG
		  && p->ref[0]->type_o == T_DISPL
		  && p->ref[0]->reg_o == SPREG ) {
		    goto delete_adjust;
		}
		break;
	    delete_adjust:
		offset = n->ref[0]->value_o - p->ref[0]->value_o;
		p = deletenode(p);
		if (offset == 0) {
		    n = deletenode(n);
		} else if (offset >= 1 && offset <= 8) {
		    n->ref[0]->value_o = offset;
		    installinstruct(n, sopcode("addql"));
		} else if (offset <= -8 && offset >= -1) {
		    n->ref[0]->value_o = -offset;
		    installinstruct(n, sopcode("subql"));
		} else {
		    o = n->ref[0];
		    o->type_o = T_DISPL;
		    o->reg_o = SPREG;
		    o->value_o = offset;
		    installinstruct(n, sopcode("lea"));
		}
		continue;
	    }
	}

	/*
	 * Pattern #1:
	 *	(b) movl  X,sp@-
	 *	...
	 *	(n) <op>  sp@+,...
	 *
	 * if possible, rewrite as
	 *	(b) (deleted)
	 *	...
	 *	(n) <op>  X,...
	 *
	 * Requirements:
	 *	X must not be a control register or i/o device.
	 *	X must not be modified in the interim.
	 *      Side effects of X must not be used in the interim.
	 */
	if (ISINSTRUC(n->op) && n->ref[0] != NULL && ispopop(n->ref[0])) {
	    b = n;
	    o = NULL;
	    do {
		previ(b);
		if (b->op == OP_FIRST || b->op == OP_LABEL
		  || ISBRANCH(b->op)) {
		    /* flow of control to reach this point is unknown */
		    break;
		}
		if (b->op == OP_PEA) {
		    switch(b->ref[0]->type_o) {
		    case T_ABSS:
		    case T_ABSL:
		    case T_NORMAL:
			/*
			 * srce is #X
			 */
			o = newoperand(b->ref[0]);
			o->type_o = T_IMMED;
			b->subop = SUBOP_L;
		    }
		    break;
		}
		if (b->op == OP_CLR && ispushop(b->ref[0])) {
		    /*
		     * srce is #0
		     */
		    o = newoperand(b->ref[0]);
		    o->type_o = T_IMMED;
		    o->value_o = 0;
		    break;
		}
		if (b->op == OP_MOVE && ispushop(b->ref[1])) {
		    o = newoperand(b->ref[0]);
		    break;
		}
		if (inmask(SPREG,b->rset) || inmask(SPREG,b->ruse)) {
		    /* unknown use/modification of sp */
		    break;
		}
	    } while(1);
	    /*
	     * If the preceding search yielded a usable operand,
	     * determine whether its use can be moved from node
	     * (b) to (n).
	     */
	    if (o != NULL) {
		l = submask(b->rset, MAKEWMASK(SPREG, LW));
		if ( o->type_o == T_REG && !datareg(o->value_o)
		  || o->type_o != T_IMMED && o->type_o != T_REG && !cancache(o)
		  || !emptymask(andmask(l, b->forw->rlive))) {
		    /*
		     * x is a control register or might be an i/o device,
		     * or has non-trivial side effects: cannot move.
		     */
		    freeoperand(o);
		    o = NULL;
		} else {
		    /* search for defs of X and defs affecting access to X */
		    l = submask(b->ruse, MAKERMASK(SPREG, LR));
		    p = n;
		    previ(p);
		    while (p != b) {
			if (may_touch(p, o, b->subop, WMASK)) {
			    freeoperand(o);	/* bail out */
			    o = NULL;
			    break;
			}
		        if (!emptymask(andmask(l, p->rset))) {
			    /*
			     * (p) defines register used at (b);  if (p)
			     * is a simple register load, and if the lifetime
			     * of the register is restricted to (p)..(n),
			     * we may be able to use a different register,
			     * eliminating the conflict
			     */
			    int r1,r2;
			    if (p->op != OP_MOVE
			      || p->ref[1]->type_o != T_REG ) {
				freeoperand(o);	/* bail out */
				o = NULL;
				break;
			    }
			    r1 = p->ref[1]->value_o;
			    r2 = find_freereg(p, n, reg_access[r1]);
			    if (r2 == -1 || !can_reassign_reg(p, n, r1, r2)) {
				freeoperand(o);	/* bail out */
				o = NULL;
				break;
			    }
			    reassign_reg(p, n, r1, r2);
			}
			previ(p);
		    }
		}
	    }
	    /*
	     * If it is feasible to move the use of the operand X,
	     * find out whether X is a suitable source operand of
	     * the instruction at (n).
	     */
	    if (o != NULL) {
		if (BYTESIZE(b->subop) == BYTESIZE(n->subop)
		  && operand_ok(n->instr, o, n->ref[1], n->ref[2])) {
		    n = moveuse(b, n, o, n->instr);
		    changes++;
		    continue;
		}
		/*
		 * if X is a floating point register, try rewriting
		 * node (n) using an extended floating point opcode
		 */
		if (o->type_o == T_REG && freg(o->value_o)) {
		    ip = extendedop(n->instr);
		    if (ip != NULL
		      && operand_ok(ip, o, n->ref[1], n->ref[2])) {
			n = moveuse(b, n, o, ip);
			changes++;
			continue;
		    }
		}
		/*
		 * if X is an addressable double operand and the
		 * the use of sp@+ occurs in a double precision
		 * floating point operation, use X directly.
		 */
		if (n->subop == SUBOP_D && o->type_o != T_REG
		  && b->back->op == OP_MOVE
		  && b->back->subop == SUBOP_L
		  && ispushop(b->back->ref[1])
		  && adjacent(o, b->back->ref[0])) {
		    (void) deletenode(b->back);
		    n = moveuse(b, n, o, n->instr);
		    changes++;
		    continue;
		}
		freeoperand(o); /* give up */
	    } /* if o != NULL */
	} /* if ISINSTRUCT... */

	/*
	 * Other patterns assume the instruction at (n) is a move.
	 */
	if (n->op != OP_MOVE)
	    continue;

	/*
	 * Pattern #2:
	 *	(b) move X,sp@-
	 *	    ....
	 *	(n) move sp@+,rn
	 * If possible, rewrite as:
	 *	(b) move X,rn
	 *	    ....
	 *	(n) (deleted)
	 * Requirements:
	 *	Rn must neither be used nor set in the interim.
	 *	If rn is an a-register, condition codes must not
	 *	be live immediately following (b).
	 *	If rn is not an a-register, condition codes must not
	 *	be live immediately following (n).
	 */
	o = n->ref[0];
	regno = n->ref[1]->value_o;
	if(ispopop(o) && n->ref[1]->type_o == T_REG
	  && !(dreg(regno) && inmask(CCREG,n->forw->rlive))
	  && !(freg(regno) && inmask(FPCCREG, n->forw->rlive))) {
	    b = n;
	    do {
		previ(b);
		if (b->op == OP_FIRST || b->op == OP_LABEL
		  || ISBRANCH(b->op)) {
		    /* flow of control to reach this point is unknown */
		    break;
		}
		if (b->op == OP_PEA) {
		    /* note: PEA does not set the condition codes */
		    if (areg(regno)) {
			/*
			 * rewrite as: lea <srce>,areg
			 */
			n = movedef(b,n,sopcode("lea"));
			changes++;
		    } else if ((r = dead_areg(b)) != -1
		      && !inmask(CCREG,b->forw->rlive)) {
			/*
			 * rewrite as:	lea <srce>,areg;
			 *		movl areg,rn
			 */
			n = movedef2(b,n,r,sopcode("lea"));
			changes++;
		    } else {
			switch(b->ref[0]->type_o) {
			case T_ABSS:
			case T_ABSL:
			case T_NORMAL:
			    /*
			     * rewrite as: movl #<srce>,reg
			     */
			    b->ref[0]->type_o = T_IMMED;
			    n = movedef(b,n,sopcode("movl"));
			    changes++;
			}
		    }
		    break;
		}
		if (b->op == OP_CLR && ispushop(b->ref[0])
		  && b->subop == SUBOP_L
		  && !(areg(regno) && inmask(CCREG,b->forw->rlive))) {
		    /*
		     * rewrite as: movl #0,reg
		     */
		    b->ref[0]->type_o = T_IMMED;
		    b->ref[0]->value_o = 0;
		    n = movedef(b,n,sopcode("movl"));
		    changes++;
		    break;
		}
		if (b->op == OP_MOVE && ispushop(b->ref[1])
		  && b->subop == SUBOP_L
		  && !(areg(regno) && inmask(CCREG,b->forw->rlive))) {
		    /*
		     * try to change into MOVE <srce>,reg
		     * beware floating point register operands
		     */
		    if (!freg(regno)) {
			/* reg is a-reg or d-reg */
			if (b->subop == SUBOP_D || b->subop == SUBOP_X) {
			    /* can't move doubles to a register pair */
			} else if (areg(regno)
			  && b->ref[0]->type_o == T_REG
			  && freg(b->ref[0]->value_o)) {
			    /* can't move fp reg to a-reg */
			} else if (b->subop == n->subop) {
			    /* easy case */
			    n = movedef(b,n,n->instr);
			    changes++;
			}
		    } else {
			o = b->ref[0];
			if (o->type_o == T_REG && freg(o->value_o)) {
			    /*
			     * freg-freg move
			     */
			    n = movedef(b,n,sopcode("fmovex"));
			} else if (b->subop == n->subop
			  && BYTESIZE(n->subop) <= sizeof(long)
			  && !(o->type_o == T_REG && areg(o->value_o))) {
			    /*
			     * move single word to freg
			     */
			    n = movedef(b,n,n->instr);
			    changes++;
			} else if (n->subop == SUBOP_D
			  && o->type_o != T_REG
			  && b->back->op == OP_MOVE
			  && b->back->subop == SUBOP_L
			  && ispushop(b->back->ref[1])
			  && adjacent(o, b->back->ref[0])) {
			    /*
			     * move addressable double operand to freg
			     */
			    (void) deletenode(b->back);
			    b->subop = SUBOP_D;
			    n = movedef(b,n,n->instr);
			    changes++;
			}
			/* else forget it! */
		    }
		    break;
		}
		if (inmask(regno,b->rset) || inmask(regno,b->ruse)) {
		    /* dest reg was used or modified along the way */
		    break;
		}
		if (inmask(SPREG,b->rset) || inmask(SPREG,b->ruse)) {
		    /* some other use/modification of sp */
		    break;
		}
	    } while(1);
	}

	/*
	 * Pattern #3:
	 *	(b) move sp@+,rn
	 *	    ....
	 *	(n) move rn,sp@-
	 *
	 * if possible, rewrite as
	 *	(b) move sp@,rn
	 *	    ....
	 *	(n) (deleted)
	 *
	 * requirements:
	 *	Rn must not be used or set in the interim.
	 *	Condition codes must not be live after either (b) or (n).
	 */
	o = n->ref[1];
	if (ispushop(o) && n->ref[0]->type_o == T_REG
	  && datareg(n->ref[0]->value_o)
	  && !inmask(CCREG,n->forw->rlive)
	  && !inmask(FPCCREG, n->forw->rlive)) {
	    regno = n->ref[0]->value_o;
	    b = n;
	    do {
		previ(b);
		if (b->op == OP_FIRST || b->op == OP_LABEL
		  || ISBRANCH(b->op)) {
		    /* flow of control to reach this point is unknown */
		    break;
		}
		if (b->op == OP_MOVE && ispopop(b->ref[0])
		  && b->subop == n->subop
		  && sameops(b->ref[1], n->ref[0])
		  && !inmask(CCREG, b->forw->rlive)
		  && !inmask(FPCCREG, b->forw->rlive)) {
		    /* pushing same value that was just popped */
		    n = deletenode(n);
		    b->ref[0]->type_o = T_DEFER;
		    installinstruct(b, b->instr);
		    l = n->forw->rlive;
		    for (p = n; p != b; p = p->back) {
			l = p->rlive = compute_normal(p, l);
		    }
		    changes++;
		    break;
		}
		if (inmask(regno,b->rset) || inmask(regno,b->ruse)) {
		    /* reg was used/modified along the way */
		    break;
		}
		if (inmask(SPREG,b->rset) || inmask(SPREG,b->ruse)) {
		    /* some other use/modification of sp */
		    break;
		}
	    } while(1);
	} /* if */

    } /* for */
    return(changes);
}

unix.superglobalmegacorp.com

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