[comp.sources.atari.st] v02i099: sozobon1.2 -- Update to Sozobon C compiler part08/09

koreth@panarthea.ebay.sun.com (Steven Grimm) (10/26/89)

Submitted-by: ncar.ucar.edu!dunike!onecom!wldrdg!hans (Johann Ruegg)
Posting-number: Volume 2, Issue 99
Archive-name: sozobon1.2/part08

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 8 (of 9)."
# Contents:  top/NPEEP2.C top/REG.C
# Wrapped by koreth@panarthea on Tue Oct 24 18:40:47 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'top/NPEEP2.C' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'top/NPEEP2.C'\"
else
echo shar: Extracting \"'top/NPEEP2.C'\" \(17335 characters\)
sed "s/^X//" >'top/NPEEP2.C' <<'END_OF_FILE'
X/* Copyright (c) 1988 by Sozobon, Limited.  Author: Tony Andrews
X *
X * Permission is granted to anyone to use this software for any purpose
X * on any computer system, and to redistribute it freely, with the
X * following restrictions:
X * 1) No charge may be made other than reasonable charges for reproduction.
X * 2) Modified versions must be clearly marked as such.
X * 3) The authors are not responsible for any harmful consequences
X *    of using this software, even if they result from defects in it.
X */
X
X/*
X * 2-instruction peephole optimizations
X */
X
X#include "top.h"
X
X/*
X * Macros to reference commonly-used values... cleans up the following
X * code quite a bit.
X */
X#define	sm1	i1->src.amode	/* source & dest addressing modes */
X#define	dm1	i1->dst.amode
X#define	sm2	i2->src.amode
X#define	dm2	i2->dst.amode
X
X#define	sr1	i1->src.areg	/* source & dest registers */
X#define	dr1	i1->dst.areg
X#define	sr2	i2->src.areg
X#define	dr2	i2->dst.areg
X
X
X/*
X * ipeep2(bp, i1) - look for 2-instruction optimizations at the given inst.
X */
Xstatic	int
Xipeep2(bp, i1)
XBLOCK	*bp;
Xregister INST	*i1;
X{
X	register INST	*i2;		/* the next instruction */
X	register INST	*ti2;		/* "temporary" next inst */
X
X	register int	op1, op2;	/* opcodes, for speed */
X
X	i2  = i1->next;
X	op1 = i1->opcode;
X	op2 = i2->opcode;
X
X	/*
X	 * Avoid stack fix-ups after a call if possible.
X	 */
X
X	/*
X	 *	addq	#4,sp
X	 *	... stuff that doesn't use SP ...
X	 *	move.l	?,-(sp)		=>	move.l	?,(sp)
X	 */
X	if (op1 == ADDQ && sm1 == IMM && i1->src.disp == 4 &&
X	    dm1 == REG && dr1 == SP) {
X
X		ti2 = i2;
X		while (!uses(ti2, SP)) {
X			if (ti2->next == NULL)
X				goto end2;
X			ti2 = ti2->next;
X		}
X
X		if (ti2->opcode == MOVE && ti2->flags == LENL &&
X		    ti2->dst.amode == (REGI|DEC) && ti2->dst.areg == SP) {
X		    	ti2->dst.amode = REGI;
X		    	delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X		    	return CLEAN;
X		}
X	}
Xend2:
X
X	/*
X	 *	addq	#2,sp
X	 *	... stuff that doesn't use SP ...
X	 *	move.w	?,-(sp)		=>	move.w	?,(sp)
X	 */
X	if (op1 == ADDQ && sm1 == IMM && i1->src.disp == 2 &&
X	    dm1 == REG && dr1 == SP) {
X
X		ti2 = i2;
X		while (!uses(ti2, SP)) {
X			if (ti2->next == NULL)
X				goto end3;
X			ti2 = ti2->next;
X		}
X
X		if (ti2->opcode == MOVE && ti2->flags == LENW &&
X		    ti2->dst.amode == (REGI|DEC) && ti2->dst.areg == SP) {
X		    	ti2->dst.amode = REGI;
X		    	delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X		    	return CLEAN;
X		}
X	}
Xend3:
X
X	/*
X	 * Avoid "tst" instructions following instructions that
X	 * set the Z flag.
X	 */
X
X	/*
X	 *	move.x	X, Y		=>	move.x	X, Y
X	 *	tst.x	X or Y			...deleted...
X	 *	beq/bne				beq/bne
X	 *
X	 *	Where Y is not An, because "movea" doesn't set the
X	 *	zero flag.
X	 */
X	if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) &&
X	    op1 == MOVE && op2 == TST &&
X	    i1->flags == i2->flags) {
X
X		/*
X		 * If pre-decrement is set on the dest. of the move,
X		 * don't let that screw up the operand comparison.
X		 */
X		if (dm1 & DEC)
X			dm1 &= ~DEC;
X
X		if (opeq(&i1->dst, &i2->src) || opeq(&i1->src, &i2->src)) {
X			if (dm1 != REG || ISD(dr1)) {
X			    	delinst(bp, i2);
X			    	DBG(printf("%d ", __LINE__))
X			    	return CLEAN;
X			}
X		}
X	}
X
X	/*
X	 *	and.x	X, Y		=>	and.x	X, Y
X	 *	tst.x	X or Y			...deleted...
X	 *	beq/bne				beq/bne
X	 *
X	 *	Where Y is not An, because "movea" doesn't set the
X	 *	zero flag.
X	 */
X	if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) &&
X	    op1 == AND && op2 == TST &&
X	    i1->flags == i2->flags) {
X
X		/*
X		 * If pre-decrement is set on the dest. of the move,
X		 * don't let that screw up the operand comparison.
X		 */
X		if (dm1 & DEC)
X			dm1 &= ~DEC;
X
X		if (opeq(&i1->dst, &i2->src) || opeq(&i1->src, &i2->src)) {
X			if (dm1 != REG || ISD(dr1)) {
X			    	delinst(bp, i2);
X			    	DBG(printf("%d ", __LINE__))
X			    	return CLEAN;
X			}
X		}
X	}
X
X	/*
X	 *	ext.x	Dn		=>	ext.x	Dn
X	 *	tst.x	Dn			...deleted...
X	 *	beq/bne				beq/bne
X	 *
X	 *	Where Y is not An, because "movea" doesn't set the
X	 *	zero flag.
X	 */
X	if ((bp->last == i2) && (bp->bcode == BEQ || bp->bcode == BNE) &&
X	    (op1 == EXT) && (op2 == TST) &&
X	    (i1->flags == i2->flags)) {
X
X		if ((sm1 == REG) && ISD(sr1) && (sm2 == REG) && (sr1 == sr2)) {
X		    	delinst(bp, i2);
X		    	DBG(printf("%d ", __LINE__))
X		    	return CLEAN;
X		}
X	}
X
X	/*
X	 *	move.?	X, Dn		=>	move.?	X, Dn
X	 *	ext.?	Dn			...deleted...
X	 *	beq/bne				beq/bne
X	 *
X	 *	Where Dn is dead after the "ext".
X	 */
X	if (bp->last == i2 && (bp->bcode == BEQ || bp->bcode == BNE) &&
X	    op1 == MOVE && op2 == EXT) {
X
X		if ((dm1 == REG) && ISD(dr1) &&
X		    (sm2 == REG) && (dr1 == sr2)) {
X			if ((i2->live & RM(sr2)) == 0) {
X				delinst(bp, i2);
X			    	DBG(printf("%d ", __LINE__))
X		 		return CLEAN;
X			}
X		}
X	}
X
X	/*
X	 *	ext.l	Dm		=>	...deleted...
X	 *	tst.l	Dm			tst.w	Dm
X	 *
X	 *	where Dm is dead after the "tst".
X	 */
X	if (op1 == EXT && op2 == TST &&
X	    ((i1->flags & LENL) != 0) &&  ((i2->flags & LENL) != 0) &&
X	    (sr1 == sr2) && ISD(sr1)) {
X
X		if ((i2->live & RM(sr2)) == 0) {
X			i2->flags = LENW;
X			delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X			return CLEAN;
X		}
X	}
X
X#if 0
X	/*
X	 *	add[q]	#?,sp
X	 *	... stuff that doesn't use SP ...
X	 *	unlk	An		=>	unlk	An
X	 */
X	if ((op1 == ADDQ || op1 == ADD) && sm1 == IMM &&
X	     dm1 == REG && dr1 == SP) {
X
X		ti2 = i2;
X		while (!uses(ti2, SP)) {
X			if (ti2->next == NULL)
X				goto end8;
X			ti2 = ti2->next;
X		}
X		if (ti2->opcode == UNLK) {
X		     	delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X		     	return CLEAN;
X		}
X	}
Xend8:
X#endif
X
X	/*
X	 *	ext.l	Dm		=>	...deleted...
X	 *	???	N(An,Dm.l), ??		???	N(An,Dm.w), ??
X	 *
X	 *	Where Dm is dead
X	 */
X	if ((op1 == EXT) && (i1->flags & LENL) &&
X	    (sm2 == (REGIDX|XLONG)) &&
X	    (sr1 == i2->src.ireg)) {
X
X		if ((i2->live & RM(sr1)) == 0) {
X			sm2 &= ~XLONG;
X			delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X			return CLEAN;
X		}
X	}
X
X	/*
X	 *	ext.l	Dm		=>	...deleted...
X	 *	???	??, N(An,Dm.l)		???	??, N(An,Dm.w)
X	 *
X	 *	Where Dm is dead
X	 */
X	if ((op1 == EXT) && (i1->flags & LENL) &&
X	    (dm2 == (REGIDX|XLONG)) &&
X	    (sr1 == i2->dst.ireg)) {
X
X		if ((i2->live & RM(sr1)) == 0) {
X			dm2  &= ~XLONG;
X			delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X			return CLEAN;
X		}
X	}
X
X	/*
X	 * Avoid intermediate registers.
X	 */
X
X	/*
X	 *	move.x	X, Dm		=>	INST.x	X, Dn
X	 *	INST.x	Dm, Dn
X	 *
X	 *	where Dm is dead, and INST is one of: add, sub, and, or, cmp
X	 */
X	if ((op1 == MOVE) &&
X	    ((op2==ADD)||(op2==SUB)||(op2==AND)||(op2==OR)||(op2==CMP)) &&
X	    (i1->flags == i2->flags) &&
X	    (dm1 == REG) && ISD(dr1) &&
X	    (sm2 == REG) && ISD(sr2) &&
X	    (dm2 == REG) && ISD(dr2) &&
X	    (dr1 == sr2)) {
X
X		if ((i2->live & RM(sr2)) == 0) {
X
X			i1->opcode = i2->opcode;
X			dr1 = dr2;
X
X			delinst(bp, i2);
X		    	DBG(printf("%d ", __LINE__))
X			return DIRTY;
X		}
X	}
X
X	/*
X	 * Silly moves
X	 */
X
X	/*
X	 *	move.x	X, Y		=>	move.x	X, Y
X	 *	move.x	Y, X
X	 */
X	if ((op1 == MOVE) && (op2 == MOVE) &&
X	    (i1->flags == i2->flags) &&
X	    opeq(&i1->src, &i2->dst) && opeq(&i1->dst, &i2->src) &&
X	    ((sm1 & (INC|DEC)) == 0) &&
X	    ((dm1 & (INC|DEC)) == 0)) {
X
X	     	delinst(bp, i2);
X	    	DBG(printf("%d ", __LINE__))
X	     	return CLEAN;
X	}
X
X	/*
X	 *	move.x	X, Y		=>	move.x	X, Rn
X	 *	move.x	Y, Rn			move.x	Rn, Y
X	 *
X	 *	where Y isn't INC or DEC, and isn't register direct
X	 */
X	if ((op1 == MOVE) && (op2 == MOVE) && (dm2 == REG) &&
X	    opeq(&i1->dst, &i2->src) && ((dm1 & (INC|DEC)) == 0) &&
X	    (i1->flags == i2->flags) && (dm1 != REG)) {
X
X		freeop(&i1->dst);
X		i1->dst = i2->dst;
X		i2->dst = i2->src;
X		i2->src = i1->dst;
X
X	    	DBG(printf("%d ", __LINE__))
X		return CLEAN;
X	}
X
X	/*
X	 *	move.x	Dm, X		=>	move.x	Dm, X
X	 *	move.x	X, Y			move.x	Dm, Y
X	 *
X	 * Where 'x' is the same, and 'X' has no side-effects.
X	 */
X	if ((op1 == MOVE) && (op2 == MOVE) &&
X	    (sm1 == REG) && ISD(sr1) &&
X	    (i1->flags == i2->flags) && opeq(&i1->dst, &i2->src) &&
X	    ((dm1 & (DEC|INC)) == 0)) {
X
X
X		freeop(&i2->src);
X		i2->src = i1->src;
X	    	DBG(printf("%d ", __LINE__))
X	     	return DIRTY;
X	}
X
X#if 0
X	/*
X	 *	move.?	Rm, Rn		=>	move.?	Rm, Rn
X	 *	... stuff ...			... stuff ...
X	 *	move.?	Rm, Rn
X	 *
X	 *	where "stuff" doesn't set Rm or Rn. Also make sure a
X	 *	conditional branch doesn't directly follow the second
X	 *	move. If so, we need to leave the move alone since it
X	 *	sets condition codes.
X	 */
X	if ((op1 == MOVE) && (sm1 == REG) && (dm1 == REG)) {
X		ti2 = i2;
X		while (ti2 != NULL && !sets(ti2, sr1) && !sets(ti2, dr1)) {
X
X			if ((ti2->opcode==MOVE) && (i1->flags==ti2->flags) &&
X			    (ti2->src.amode==REG) && (ti2->dst.amode==REG) &&
X			    (sr1 == ti2->src.areg) &&
X			    (dr1 == ti2->dst.areg)) {
X
X			     	delinst(bp, ti2);
X			    	DBG(printf("%d ", __LINE__))
X			     	return DIRTY;
X			}
X			ti2 = ti2->next;
X		}
X	}
X#endif
X
X	/*
X	 *	move.l	Am, Dn		=>	move.l	Am, Ao
X	 *	... stuff ...			... stuff ...
X	 *	move.l	Dn, Ao
X	 *
X	 *	where "stuff" doesn't set Dn.
X	 */
X	if ((op1 == MOVE) && (i1->flags == LENL) &&
X	    (sm1 == REG) && ISA(sr1) && (dm1 == REG) && ISD(dr1)) {
X
X		ti2 = i2;
X		while (!sets(ti2, dr1)) {
X
X			if ((ti2->opcode == MOVE) && (ti2->flags == LENL) &&
X			    (ti2->src.amode == REG) && ISD(ti2->src.areg) &&
X			    (ti2->dst.amode == REG) && ISA(ti2->dst.areg) &&
X			    (dr1 == ti2->src.areg)) {
X	
X				/*
X				 * If the intermediate register isn't dead,
X				 * then we have to keep using it.
X				 */
X				if ((ti2->live & RM(ti2->src.areg)) != 0)
X					goto end14;
X	
X				dr1 = ti2->dst.areg;
X	
X			     	delinst(bp, ti2);
X			    	DBG(printf("%d ", __LINE__))
X			     	return DIRTY;
X			}
X
X			if (ti2->next == NULL)
X				goto end14;
X
X			ti2 = ti2->next;
X		}
X	}
Xend14:
X
X	/*
X	 *	move.l	Dm, An		=>	move.l	Dm, Ao
X	 *	lea	(An), Ao
X	 *
X	 *	where An is dead
X	 */
X	if ((op1 == MOVE) && (op2 == LEA) && (sm1 == REG) && ISD(sr1) &&
X	    (dm1 == REG) && ISA(dr1) && (sm2 == REGI) && (dm2 == REG) &&
X	    ISA(dr2) && (dr1 == sr2)) {
X
X		if ((i2->live & RM(sr2)) == 0) {
X
X			dr1 = dr2;
X			i1->live = i2->live;
X
X			delinst(bp, i2);
X		    	DBG(printf("%d ", __LINE__))
X			return CLEAN;
X		}
X	}
X
X	/*
X	 *	lea	X, An		=>	lea	X, Ao
X	 *	lea	(An), Ao
X	 *
X	 *	where An is dead
X	 */
X	if ((op1 == LEA) && (op2 == LEA) && (sm2 == REGI) && (dr1 == sr2)) {
X
X		if ((i2->live & RM(sr2)) == 0) {
X
X			dr1 = dr2;
X			i1->live = i2->live;
X
X			delinst(bp, i2);
X		    	DBG(printf("%d ", __LINE__))
X			return CLEAN;
X		}
X	}
X
X	/*
X	 *	lea	N(Am), Am	=>
X	 *	?	(Am)[,...]		?	N(Am)[,...]
X	 *
X	 *	Where Am is either dead after the second instruction or
X	 *	is a direct destination of the second instruction.
X	 */
X	if ((op1 == LEA) && (sm1 == REGID) && (sr1 == dr1) &&
X	    (sm2 == REGI) && (dr1 == sr2)) {
X
X		if (((i2->live & RM(sr2)) == 0) ||
X		   ((dm2 == REG) && (dr2 == sr2))) {
X			i2->src.amode = REGID;
X			i2->src.disp = i1->src.disp;
X			delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X			return DIRTY;
X		}
X	}
X
X	/*
X	 *	lea	N(Am), Am	=>
X	 *	?	X, (Am)			?	X, N(Am)
X	 *
X	 *	Where X doesn't reference Am, and Am is dead after the
X	 *	second instruction.
X	 */
X	if ((op1 == LEA) && (sm1 == REGID) &&
X	    (sr1 == dr1) && (dm2 == REGI) && (dr1 == dr2)) {
X
X		if (((i2->live & RM(dr2)) == 0) &&
X		    ((sm2 == IMM) || (sm2 == ABS) || (sr2 != dr2))) {
X			dm2 = REGID;
X			i2->dst.disp = i1->src.disp;
X			delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X			return DIRTY;
X		}
X	}
X
X	/*
X	 *	lea	X, Am		=>	...deleted...
X	 *	clr.x	(Am)			clr.x	X
X	 *
X	 *	where Am is dead
X	 */
X	if ((op1 == LEA) && (op2 == CLR) && (sm2 == REGI) && (dr1 == sr2)) {
X
X		if ((i2->live & RM(sr2)) == 0) {
X			i2->src = i1->src;
X	
X			delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X			return CLEAN;
X		}
X	}
X
X	/*
X	 *	lea	X, Am		=>	...deleted...
X	 *	move.x	Y, (Am)			move.x	Y, X
X	 *
X	 *	where Am is dead
X	 */
X	if ((op1 == LEA) && (op2 == MOVE) && dm2 == REGI && (dr1 == dr2)) {
X
X		if ((i2->live & RM(dr2)) == 0) {
X			i2->dst = i1->src;
X
X			delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X			return CLEAN;
X		}
X	}
X
X	/*
X	 *	lea	X, Am		=>	...deleted...
X	 *	move.x	(Am), Y			move.x	X, Y
X	 *
X	 *	where Am is dead
X	 */
X	if ((op1 == LEA) && (op2 == MOVE) && sm2 == REGI && (dr1 == sr2)) {
X
X		if ((i2->live & RM(sr2)) == 0) {
X			i2->src = i1->src;
X
X			delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X			return CLEAN;
X		}
X	}
X
X	/*
X	 *	move.x	Dm, X		=>	move.x	Dm, X
X	 *	cmp.x	#N, X			cmp.x	#N, Dm
X	 *
X	 *	Where X isn't register direct.
X	 *
X	 *	Since X generally references memory, we can compare
X	 *	with the register faster.
X	 */
X	if ((op1 == MOVE) && (op2 == CMP) &&
X	    (i1->flags == i2->flags) && (sm2 == IMM) &&
X	    (sm1 == REG) && ISD(sr1) && (dm1 != REG) &&
X	    opeq(&i1->dst, &i2->dst) && ((dm1 & (INC|DEC)) == 0)) {
X
X		freeop(&i2->dst);
X		dm2 = REG;
X		dr2 = sr1;
X
X	    	DBG(printf("%d ", __LINE__))
X		return DIRTY;
X	}
X
X
X	/*
X	 * Try to use register indirect w/ displacement and/or index
X	 */
X
X	/*
X	 *	add.l	Am, Dn		=>	lea	0(Am,Dn.l), Ao
X	 *	move.l	Dn, Ao
X	 *
X	 *	where Dn is dead
X	 */
X	if ((op1 == ADD) && (op2 == MOVE) &&
X	    (sm1 == REG) && ISA(sr1) &&
X	    (dm1 == REG) && ISD(dr1) &&
X	    (sm2 == REG) && ISD(sr2) &&
X	    (dm2 == REG) && ISA(dr2) &&
X	    (dr1 == sr2) &&
X	    (i1->flags & LENL) && (i2->flags & LENL)) {
X
X		if ((i2->live & RM(sr2)) == 0) {
X
X			i2->opcode = LEA;
X			i2->flags = 0;
X
X			sm2 = REGIDX|XLONG;
X			i2->src.disp = 0;
X			sr2 = sr1;
X			i2->src.ireg = dr1;
X
X		    	delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X		    	return CLEAN;
X		}
X	}
X
X	/*
X	 *	add.l	Dm, An		=>	move.x	0(An,Dm.l), Do
X	 *	move.x	(An), Do
X	 *
X	 *	where An is dead
X	 */
X	if ((op1 == ADD) && (op2 == MOVE) &&
X	    (sm1 == REG) && ISD(i1->src.areg) &&
X	    (dm1 == REG) && ISA(dr1) &&
X	    (sm2 == REGI)&& ISA(sr2) &&
X	    (dm2 == REG) && ISD(dr2) &&
X	    (dr1 == sr2) && (i1->flags & LENL)) {
X
X		if ((i2->live & RM(sr2)) == 0) {
X
X			sm2 = REGIDX|XLONG;
X			i2->src.disp = 0;
X			i2->src.ireg = sr1;
X
X		    	delinst(bp, i1);
X		    	DBG(printf("%d ", __LINE__))
X		    	return CLEAN;
X		}
X	}
X
X	/*
X	 *	lea	N(Am), An	=>	lea	N(Am,Do.l), An
X	 *	add.l	Do, An
X	 *
X	 */
X	if ((op1 == LEA) && (op2 == ADD) && (sm1 == REGID) &&
X	    (sm2 == REG) && ISD(sr2) && (dm2 == REG) && ISA(dr2) &&
X	    (dr1 == dr2) && D8OK(i1->src.disp)) {
X
X		sm1 = REGIDX|XLONG;
X		i1->src.ireg = sr2;
X		i1->live = i2->live;
X		delinst(bp, i2);
X	    	DBG(printf("%d ", __LINE__))
X		return CLEAN;
X	}
X
X
X
X	/*
X	 * Try to use the pre-decrement and post-increment modes
X	 * whenever possible.
X	 */
X
X	/*
X	 *	sub.l	#1, Am
X	 *	... stuff ...
X	 *	???.b	..(Am)..	=>	???.b	..-(Am)..
X	 *
X	 *	Nothing in "stuff" can refer to Am.
X	 */
X	if ((op1 == SUBQ) && (i1->flags & LENL) &&
X	    (sm1 == IMM) && (i1->src.disp == 1) &&
X	    (dm1 == REG) && ISA(dr1)) {
X
X		while (i2 != NULL) {
X
X			if (sm2 == REGI && sr2 == dr1) {
X
X				if ((i2->flags & LENB) == 0)
X					goto end24;
X
X				sm2 |= DEC;
X
X			    	delinst(bp, i1);
X			    	DBG(printf("%d ", __LINE__))
X			    	return DIRTY;
X			}
X			if (dm2 == REGI && dr2 == dr1) {
X
X				if ((i2->flags & LENB) == 0)
X					goto end24;
X
X				dm2 |= DEC;
X
X			    	delinst(bp, i1);
X			    	DBG(printf("%d ", __LINE__))
X			    	return CLEAN;
X			}
X
X			if (uses(i2, RM(dr1)))
X				goto end24;
X
X			if (i2->next == NULL)
X				goto end24;
X			else
X				i2 = i2->next;
X
X		}
X	}
Xend24:
X
X	/*
X	 *	sub.l	#2, Am
X	 *	... stuff ...
X	 *	???.w	..(Am)..	=>	???.w	..-(Am)..
X	 *
X	 *	Nothing in "stuff" can refer to Am.
X	 */
X	if ((op1 == SUBQ) && (i1->flags & LENL) &&
X	    (sm1 == IMM) && (i1->src.disp == 2) &&
X	    (dm1 == REG) && ISA(dr1)) {
X
X		while (i2 != NULL) {
X
X			if (sm2 == REGI && sr2 == dr1) {
X
X				if ((i2->flags & LENW) == 0)
X					goto end26;
X
X				sm2 |= DEC;
X
X			    	delinst(bp, i1);
X			    	DBG(printf("%d ", __LINE__))
X			    	return CLEAN;
X			}
X			if (dm2 == REGI && dr2 == dr1) {
X
X				if ((i2->flags & LENW) == 0)
X					goto end26;
X
X				dm2 |= DEC;
X
X			    	delinst(bp, i1);
X			    	DBG(printf("%d ", __LINE__))
X			    	return CLEAN;
X			}
X
X			if (uses(i2, RM(dr1)))
X				goto end26;
X
X			if (i2->next == NULL)
X				goto end26;
X			else
X				i2 = i2->next;
X
X		}
X	}
Xend26:
X
X	/*
X	 *	sub.l	#4, Am
X	 *	... stuff ...
X	 *	???.l	..(Am)..	=>	???.l	..-(Am)..
X	 *
X	 *	Nothing in "stuff" can refer to Am.
X	 */
X	if ((op1 == SUBQ) && (i1->flags & LENL) &&
X	    (sm1 == IMM) && (i1->src.disp == 4) &&
X	    (dm1 == REG) && ISA(dr1)) {
X
X		while (i2 != NULL) {
X
X			if (sm2 == REGI && sr2 == dr1) {
X
X				if ((i2->flags & LENL) == 0)
X					goto end28;
X
X				sm2 |= DEC;
X
X			    	delinst(bp, i1);
X			    	DBG(printf("%d ", __LINE__))
X			    	return CLEAN;
X			}
X			if (dm2 == REGI && dr2 == dr1) {
X
X				if ((i2->flags & LENL) == 0)
X					goto end28;
X
X				i2->dst.amode |= DEC;
X
X			    	delinst(bp, i1);
X			    	DBG(printf("%d ", __LINE__))
X			    	return CLEAN;
X			}
X
X			if (uses(i2, RM(dr1)))
X				goto end28;
X
X			if (i2->next == NULL)
X				goto end28;
X			else
X				i2 = i2->next;
X
X		}
X	}
Xend28:
X
X	return UNCHANGED;
X}
X
X/*
X * peep2(bp) - scan blocks starting at 'bp'
X */
Xint
Xpeep2(bp)
Xregister BLOCK	*bp;
X{
X	register INST	*ip;
X	register bool	i;
X	register int	state = UNCHANGED;
X
X	DBG(printf("p2 :"))
X
X	for (; bp != NULL ;bp = bp->next) {
X		for (ip = bp->first; ip != NULL && ip->next != NULL ;) {
X			if ((i = ipeep2(bp, ip)) != UNCHANGED) {
X
X				if (i == DIRTY)
X					state = DIRTY;
X				if (state == UNCHANGED)
X					state = CLEAN;
X
X				s_peep2++;
X				/*
X				 * If we had a match, then either instruction
X				 * may have been deleted, so the safe thing to
X				 * do is to go to the next block.
X				 */
X				break;
X			} else
X				ip = ip->next;
X		}
X	}
X	DBG(printf("\n"); fflush(stdout))
X	return state;
X}
END_OF_FILE
if test 17335 -ne `wc -c <'top/NPEEP2.C'`; then
    echo shar: \"'top/NPEEP2.C'\" unpacked with wrong size!
fi
# end of 'top/NPEEP2.C'
fi
if test -f 'top/REG.C' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'top/REG.C'\"
else
echo shar: Extracting \"'top/REG.C'\" \(17767 characters\)
sed "s/^X//" >'top/REG.C' <<'END_OF_FILE'
X/* Copyright (c) 1989 by Sozobon, Limited.  Author: Tony Andrews
X *
X * Permission is granted to anyone to use this software for any purpose
X * on any computer system, and to redistribute it freely, with the
X * following restrictions:
X * 1) No charge may be made other than reasonable charges for reproduction.
X * 2) Modified versions must be clearly marked as such.
X * 3) The authors are not responsible for any harmful consequences
X *    of using this software, even if they result from defects in it.
X */
X
X/*
X * The code in this file deals with "registerizing" local variables and
X * parameters. The general idea is to look for highly referenced local
X * variables and parameters and effectively turn them into register
X * variables automatically. Only the D registers are used, currently, so
X * for pointer variables, a manual "register" declaration in the source
X * code is actually better.
X *
X * We need to be certain of several things about a variable before placing
X * it in a register. It's address must not be taken, and it must not be
X * referred to through "aliases" (e.g. when casting to a shorter object).
X * It must be able to fit in a register. And to keep things like printf from
X * breaking, parameters can only be registerized if none of the parameters
X * have their address taken.
X *
X * The compiler makes this all possible by placing "hints" within the
X * generated assembly code. These hints appear as comments, but are parsed
X * by the optimizer, and the information is stashed away by calling addvar().
X * The hints give us the size and offset of each parameter and local variable.
X * Their names are also given, although that information isn't needed here.
X *
X * There are tradeoffs to be wary of when registerizing. If no register
X * variables exist yet, then "movem" instructions have to be added, requiring
X * more references to make this worthwhile. In the case of parameters, the
X * register has to be initialized from the stack. The four cases are:
X *
X *	Locals	w/ other regs:	1 reference  required
X *		no other regs:	4 references required
X *	Parms	w/ other regs:	2 references required
X *		no other regs:	6 references required
X *
X * The numbers above represent the break-even point based on a savings of
X * 2 bytes per reference, and the incremental cost of adding "movem" or
X * "move" instructions as needed.
X *
X * This optimizes for space only. To optimize for time, each reference would
X * be weighted based on the loop nesting level at which it occurs.
X */
X
X#include "top.h"
X
X#define	MAXLOCALS	100
X
Xstatic	struct	linfo {
X	long	offset;		/* offset from A6 */
X	int	size;		/* size of the object */
X	int	ref;		/* # of references to the local */
X	int	reg;		/* reg. we assigned it to */
X	int	flags;		/* length, etc. */
X} locals[MAXLOCALS];
X
X#define	ALIASED		0x1	/* offset is aliased with another */
X#define	ADDR_TAKEN	0x2	/* address of the variable was taken */
X
X#define	IS_LOCAL(x)	(locals[(x)].offset < 0)
X#define	IS_PARM(x)	(locals[(x)].offset > 0)
X
Xstatic	bool	paddr;		/* address of a parameter was taken */
Xstatic	int	lcnt;		/* number of local variables we've seen */
Xstatic	int	rcnt;		/* number of locals that got registerized */
X
Xstatic	int	omask, nmask;	/* old and new register masks */
X
X/*
X * addvar(size, off) - add a variable entry for the current function
X *
X * These come from hints the compiler gives us about local variables.
X * We use the size and offset here to make sure we don't have aliasing
X * problems with the local variables we want to registerize.
X */
Xvoid
Xaddvar(size, off)
Xint	size;
Xint	off;
X{
X	locals[lcnt].offset = off;
X	locals[lcnt].size = size;
X	locals[lcnt].flags = 0;
X	locals[lcnt].ref = 0;
X
X	lcnt++;
X}
X
X/*
X * clrvar() - clear the variable list
X */
Xvoid
Xclrvar()
X{
X	register int	i;
X
X	/*
X	 * re-initialize the local information
X	 */
X	for (i=0; i < MAXLOCALS ;i++) {
X		locals[i].ref = 0;
X		locals[i].reg = -1;
X		locals[i].flags = 0;
X		locals[i].offset = 0;
X		locals[i].size = 0;
X	}
X	paddr = FALSE;
X	rcnt = lcnt = 0;
X}
X
X/*
X * setreg() - try to "registerize" local variables in the given function
X */
Xvoid
Xsetreg(bp)
XBLOCK	*bp;
X{
X	void	lcheck(), lassign(), lrewrite();
X
X	lcheck(bp);
X	lassign();
X
X#ifdef	DEBUG
X	if (debug)
X		dump_table();
X#endif
X
X	if (rcnt > 0)
X		lrewrite(bp);
X
X	s_reg += rcnt;		/* keep totals for accounting */
X}
X
X/*
X * lcheck() - scan for local variable references in the given function
X */
Xstatic void
Xlcheck(bp)
XBLOCK	*bp;
X{
X	void	ckref();
X	register int	i;
X	register BLOCK	*cb;
X	register INST	*ci;
X
X	for (cb = bp; cb != NULL ;cb = cb->next) {
X		for (ci = cb->first; ci != NULL ;ci = ci->next) {
X			ckref(ci, &ci->src);
X			ckref(ci, &ci->dst);
X		}
X	}
X
X	/*
X	 * Now figure out which registers are currently used.
X	 */
X	ci = bp->first->next;
X
X	if (ci != NULL && ci->opcode == MOVEM) {
X		if (ci->src.amode == REG)
X			omask = RM(ci->src.areg);
X		else
X			omask = stomask(ci->src.astr);
X	} else
X		omask = 0;
X}
X
X/*
X * ckref() - check for a local variable reference
X *
X * If a local variable reference is found, it's added to the table or
X * (if already there) its reference count is incremented. If we're
X * taking its address, note that too.
X */
Xstatic void
Xckref(ip, op)
XINST	*ip;
Xstruct	opnd	*op;
X{
X	register int	i;
X	register int	sz;
X
X	if (op->amode != REGID || op->areg != A6)
X		return;
X
X	switch (ip->flags) {
X	case LENL:
X		sz = 4;
X		break;
X	case LENW:
X		sz = 2;
X		break;
X	case LENB:
X	default:		/* for LEA and PEA */
X		sz = 1;
X		break;
X	}
X
X	/*
X	 * is the local variable already in the table?
X	 */
X	for (i=0; i < lcnt ;i++) {
X		if (locals[i].offset == op->disp && locals[i].size == sz) {
X			locals[i].ref++;
X			break;
X		}
X	}
X
X	/*
X	 * If not in the table, add an entry for it. If we add an entry
X	 * here, it must be an alias for one of the entries we got via
X	 * the compiler hints.
X	 */
X	if (i == lcnt) {
X		locals[lcnt].offset = op->disp;
X		locals[lcnt].size = sz;
X		locals[lcnt].flags = 0;
X		locals[lcnt].ref = 1;
X
X		lcnt++;
X	}
X
X	if (ip->opcode == LEA || ip->opcode == PEA) {
X		locals[i].flags = ADDR_TAKEN;
X		/*
X		 * If we took the address of a parameter, note that
X		 * by setting 'paddr'.
X		 */
X		if (IS_PARM(i))
X			paddr = TRUE;
X	}
X}
X
X/*
X * lassign() - assign local variable to registers
X *
X * Check for aliases, sort the table, and then decide how to assign
X * the local variables to registers.
X */
Xstatic void
Xlassign()
X{
X	void	ck_aliases(), sort_table(), do_sort();
X	register int	i;
X	register int	r;
X	int	minlref;	/* min. required references for a local */
X	int	minpref;	/* min. required references for a parameter */
X
X	ck_aliases();		/* disqualify any "aliased" references */
X	sort_table();		/* and sort by reference count */
X
X	/*
X	 * If there were already "movem" instructions, then we should
X	 * convert as many locals as possible to registers. If we're
X	 * going to have to add the movem's, then we need at least 4
X	 * references for this to be worthwhile. The 2 movem instructions
X	 * take 8 bytes, and each reference conversion saves 2 bytes.
X	 * This analysis optimizes for size.
X	 */
X	minlref = (omask != 0) ? 1 : 4;
X	minpref = (omask != 0) ? 2 : 6;
X
X	nmask = omask;
X
X	for (i=0, r=D3; r <= D7 ;) {
X
X		/*
X		 * If the register is already in use, skip it.
X		 */
X		if (omask & RM(r)) {
X			r++;
X			continue;
X		}
X
X		/*
X		 * If no more eligible variables, then stop.
X		 */
X		if (locals[i].ref <= 0)
X			break;
X
X		/*
X		 * If something meets the minimums, then assign it to
X		 * the current register, and adjust the minimums.
X		 */
X		if ((IS_LOCAL(i) && locals[i].ref >= minlref) ||
X		    (IS_PARM(i)  && locals[i].ref >= minpref)) {
X			locals[i].reg = r;
X			nmask |= RM(r);
X			minlref = 1;
X			minpref = 2;
X			r++;
X			i++;
X		} else {
X			/*
X			 * If we run into something that isn't referenced
X			 * enough, disqualify it and re-sort. There might
X			 * still be something else worth doing.
X			 */
X			locals[i].ref = -locals[i].ref;
X			do_sort();
X		}
X	}
X	rcnt = i;
X}
X
X/*
X * ck_aliases() - check for aliases in the locals table
X *
X * An alias occurs when two different offsets off of A6 both reference
X * the same local. This can happen when casting to a smaller type. Since
X * these references would be a pain to rewrite, we just bag it.
X */
Xstatic void
Xck_aliases()
X{
X	static	bool	ck_aref();
X	register int	i;
X	register int	s;
X	register long	d;
X
X	for (i=0; i < lcnt ;i++) {
X		d = locals[i].offset;
X		s = locals[i].size;
X
X		if (ck_aref(d, s))
X			locals[i].flags |= ALIASED;
X	}
X}
X
X/*
X * ck_aref() - check for an aliased reference
X */
Xstatic bool
Xck_aref(d, len)
Xregister long	d;
Xregister int	len;
X{
X	register int	i;
X
X	for (i=0; i < lcnt ;i++) {
X		if (locals[i].offset == d && locals[i].size == len)
X			continue;
X
X		if (overlaps(d, len, locals[i].offset, locals[i].size)) {
X			locals[i].flags |= ALIASED;
X			return TRUE;
X		}
X	}
X	return FALSE;
X}
X
Xstatic bool
Xoverlaps(d1, s1, d2, s2)
Xregister long	d1, d2;
Xint		s1, s2;
X{
X	register long	e1, e2;
X
X	e1 = d1 + s1 - 1;
X	e2 = d2 + s2 - 1;
X
X	if (d1 >= d2 && d1 <= e2)	/* d1 inside d2 <=> e2 */
X		return TRUE;
X
X	if (e1 >= d2 && e1 <= e2)	/* e1 inside d2 <=> e2 */
X		return TRUE;
X
X	return FALSE;
X}
X
Xstatic void
Xsort_table()
X{
X	register int	i;
X
X	/*
X	 * Remove uninteresting references from consideration:
X	 *
X	 * 1. Variables whose address was taken, or are aliased with another.
X	 * 2. Variables that don't fit in a register.
X	 */
X	for (i=0; i < lcnt ;i++) {
X		if (locals[i].flags&(ADDR_TAKEN|ALIASED) || locals[i].size > 4)
X			locals[i].ref = -locals[i].ref;
X	}
X
X	/*
X	 * If paddr is set, remove any parameters from consideration. We
X	 * have to do this so that things like printf (that take the address
X	 * of a parameter and increment it) don't break. Only if no parameter
X	 * addresses are taken, can we consider registerizing any of them.
X	 */
X	if (paddr) {
X		for (i=0; i < lcnt ;i++) {
X			if (IS_PARM(i) && (locals[i].ref > 0))
X				locals[i].ref = -locals[i].ref;
X		}
X	}
X
X	do_sort();
X}
X
Xstatic void
Xdo_sort()
X{
X	register int	i;
X	struct	linfo	l;
X
X	/*
X	 * simple bubble sort
X	 */
X	for (i=0; i < (lcnt-1) ;) {
X		if (locals[i].ref < locals[i+1].ref) {
X			l = locals[i];
X			locals[i] = locals[i+1];
X			locals[i+1] = l;
X			if (i > 0)
X				i--;
X		} else
X			i++;
X	}
X}
X
X/*
X * lrewrite() - rewrite the function based on the new register assignments
X *
X * Fixing the references is easy, but we have to fix up (or add) the movem
X * instructions as well. Also, we call addinits() to initialize any registers
X * that will contain parameters.
X */
Xstatic void
Xlrewrite(bp)
XBLOCK	*bp;
X{
X	void	fixref(), fixmove(), addmovem();
X	INST	*findlnk();
X	register int	i;
X	register BLOCK	*cb;
X	register INST	*ci;
X
X	/*
X	 * First, rewrite all the references to the locals that
X	 * we've reassigned to registers.
X	 */
X	for (cb = bp; cb != NULL ;cb = cb->next) {
X		for (ci = cb->first; ci != NULL ;ci = ci->next) {
X			fixref(&ci->src);
X			fixref(&ci->dst);
X		}
X	}
X
X	/*
X	 * If the movem's are there, just find them and fix up the
X	 * register specs.
X	 */
X	ci = bp->first->next;
X	if (ci != NULL && ci->opcode == MOVEM) {
X
X		/*
X		 * First, add the initialization instructions.
X		 */
X		addinits(bp, bp->first->next);
X
X		fixmove(&ci->src);
X
X		for (cb = bp; cb != NULL ;cb = cb->next) {
X			if (cb->flags & B_RET) {
X				for (ci=cb->last; ci != NULL ;ci=ci->prev) {
X					if (ci->opcode == MOVEM) {
X						fixmove(&ci->dst);
X						return;
X					}
X				}
X			}
X		}
X		return;
X	}
X
X#ifdef	DEBUG
X	if (debug)
X		printf("adding movem instructions\n");
X#endif
X	/*
X	 * There aren't any movem instructions, so we have to add
X	 * them here. What a pain...
X	 */
X	addmovem(bp, findlnk(bp), TRUE);
X	addinits(bp, findlnk(bp)->next);
X
X	for (cb = bp; cb != NULL ;cb = cb->next) {
X		if (cb->last->opcode == RTS) {
X			for (ci=cb->last; ci != NULL ;ci=ci->prev) {
X				if (ci->opcode == UNLK) {
X					addmovem(cb, ci, FALSE);
X					return;
X				}
X			}
X		}
X	}
X	/*
X	 * Reaching this point would be an error, you'd think. It means
X	 * we didn't find the exit from this function. Strangely enough,
X	 * this can actually happen in routines with infinite loops.
X	 * Since the "return" block isn't reachable, branch optimization
X	 * actually removes it. So we can't consider it an error here if
X	 * we don't find any "unlk" instruction.
X	 */
X}
X
Xstatic void
Xfixmove(op)
Xstruct	opnd	*op;
X{
X	char	*masktos();
X
X	freeop(op);
X	op->amode = ABS;
X	op->astr = strsave(masktos(nmask));
X}
X
X/*
X * findlnk() - find the LINK instruction in the given block
X *
X * When profiling, the LINK isn't the first instruction in the entry
X * block. This function lets us handle both cases cleanly.
X */
Xstatic	INST *
Xfindlnk(bp)
XBLOCK	*bp;
X{
X	INST	*ip;
X
X	for (ip=bp->first; ip != NULL ;ip = ip->next) {
X		if (ip->opcode == LINK)
X			return ip;
X	}
X	return NULL;
X}
X
Xstatic void
Xaddmovem(bp, ip, is_entry)
XBLOCK	*bp;			/* block where we're working */
XINST	*ip;			/* instruction before or after the movem */
Xbool	is_entry;		/* true if we're doing the entry code */
X{
X	char	*masktos();
X	register INST	*ni;
X	struct	opnd	*op;
X	register int	i;
X
X	if (ip == NULL)		/* no LINK found */
X		return;
X
X	/*
X	 * Allocate and initialize a new instruction
X	 */
X	ni = (INST *) alloc(sizeof(INST));
X
X	ni->flags = LENL;
X	ni->opcode = MOVEM;
X	ni->live = 0;
X	ni->rref = ni->rset = 0;
X
X	ni->src.areg = ni->dst.areg = 0;
X	ni->src.ireg = ni->dst.ireg = 0;
X	ni->src.disp = ni->dst.disp = 0;
X
X	/*
X	 * Set up the SP reference
X	 */
X	op = (is_entry) ? &ni->dst : &ni->src;
X	op->amode = (is_entry) ? REGI|DEC : REGI|INC;
X	op->areg = SP;
X
X	/*
X	 * Set up the register spec operand
X	 */
X	op = (is_entry) ? &ni->src : &ni->dst;
X
X	op->amode = ABS;
X	op->astr = strsave(masktos(nmask));
X
X	/*
X	 * If there's only one register being used, we should really
X	 * change the operand to be register direct. This way, the
X	 * peephole optimization will turn the "movem" into a simple
X	 * "move". Since we're adding an instruction here, we really
X	 * need to make it as painless as possible.
X	 */
X	if (rcnt == 1) {
X		free(op->astr);
X		op->amode = REG;
X
X		for (i=D0; i <= D7 ;i++) {
X			if (nmask & RM(i)) {
X				op->areg = i;
X				break;
X			}
X		}
X	}
X
X	/*
X	 * Link the instruction into the block
X	 */
X	if (is_entry) {
X		ni->next = ip->next;	/* link the MOVEM to its neighbors */
X		ni->prev = ip;
X
X		ip->next = ni;		/* link its neighbors to the MOVEM */
X
X		if (bp->last == ip)
X			bp->last = ni;
X		else
X			ni->next->prev = ni;
X	} else {
X		ni->next = ip;		/* link the MOVEM to its neighbors */
X		ni->prev = ip->prev;
X
X		ip->prev = ni;		/* link its neighbors to the MOVEM */
X
X		if (bp->first == ip)
X			bp->first = ni;
X		else
X			ni->prev->next = ni;
X	}
X}
X
Xstatic void
Xaddinits(bp, ip)
XBLOCK	*bp;			/* block where we're working */
XINST	*ip;			/* instruction before the moves */
X{
X	char	*masktos();
X	register INST	*ni;
X	struct	opnd	*op;
X	register int	i;
X
X	if (ip == NULL)		/* no LINK found */
X		return;
X
X	for (i=0; i < rcnt ;i++) {
X		/*
X		 * If it's a local variable, we don't have to do anything.
X		 */
X		if (IS_LOCAL(i))
X			continue;
X
X		/*
X		 * Allocate and initialize a new instruction
X		 */
X		ni = (INST *) alloc(sizeof(INST));
X	
X		switch (locals[i].size) {
X		case 1:
X			ni->flags = LENB;
X			break;
X		case 2:
X			ni->flags = LENW;
X			break;
X		case 4:
X			ni->flags = LENL;
X			break;
X		default:
X			fprintf(stderr, "Invalid length\n");
X			exit(1);
X		}
X
X		ni->opcode = MOVE;
X		ni->live = 0;
X		ni->rref = ni->rset = 0;
X		ni->src.ireg = ni->dst.ireg = 0;
X	
X		/*
X		 * Set up the variable reference.
X		 */
X		ni->src.amode = REGID;
X		ni->src.areg  = A6;
X		ni->src.disp  = locals[i].offset;
X	
X		/*
X		 * Set up the register spec operand
X		 */
X		ni->dst.amode = REG;
X		ni->dst.areg  = locals[i].reg;
X		ni->dst.disp  = 0;
X
X		/*
X		 * Link the instruction into the block
X		 */
X		ni->next = ip->next;	/* link MOVE to its neighbors */
X		ni->prev = ip;
X
X		ip->next = ni;		/* link neighbors to the MOVE */
X
X		if (bp->last == ip)
X			bp->last = ni;
X		else
X			ni->next->prev = ni;
X	}
X}
X
Xstatic void
Xfixref(op)
Xstruct	opnd	*op;
X{
X	register int	i;
X
X	if (op->amode != REGID || op->areg != A6)
X		return;
X
X	/*
X	 * Does the reference need to be changed?
X	 */
X	for (i=0; i < rcnt ;i++) {
X		if (locals[i].offset == op->disp) {
X			op->amode = REG;
X			op->areg = locals[i].reg;
X			return;
X		}
X	}
X}
X
X/*
X * stomask() - convert a register list to a mask
X *
X * Convert a string like "Rm-Rn/Ro-Rp" or "Rm-Rn" to the appropriate
X * mask value.
X */
Xstatic int
Xstomask(s)
Xchar	*s;
X{
X	register int	mask;
X	register char	*p;
X
X	mask = dorspec(s);
X
X	for (p=s; *p && *p != '/' ;p++)
X		;
X
X	if (*p == '/')
X		mask |= dorspec(p+1);
X
X	return mask;
X}
X
X/*
X * dorspec() - convert a partial register spec
X *
X * Convert a string like "Rm" or "Rm-Rn" to a mask.
X */
Xstatic int
Xdorspec(s)
Xregister char	*s;
X{
X	register int	base;
X	register int	m, n;
X	register int	mask;
X
X	base = (s[0] == 'd') ? D0 : A0;
X
X	m = s[1] - '0' + base;
X
X	if (s[2] != '-')
X		return RM(m);
X
X	n = s[4] - '0' + base;
X
X	for (mask=0; m <= n ;m++)
X		mask |= RM(m);
X
X	return mask;
X}
X
X/*
X * masktos() - convert a register mask to a descriptive string
X *
X * Generates a string of the form "Rm/Rn/Ro/..."
X */
Xstatic char *
Xmasktos(mask)
Xregister int	mask;
X{
X	static	char	buf[64];
X	register char	*p = buf;
X	register int	r;
X
X	for (r = D0; r <= D7 ;r++) {
X		if (mask & RM(r)) {
X			if (p != buf)
X				*p++ = '/';
X			*p++ = 'd';
X			*p++ = (r - D0) + '0';
X		}
X	}
X	for (r = A0; r <= A7 ;r++) {
X		if (mask & RM(r)) {
X			if (p != buf)
X				*p++ = '/';
X			*p++ = 'a';
X			*p++ = (r - A0) + '0';
X		}
X	}
X	*p = '\0';
X
X	return buf;
X}
X
X#ifdef	DEBUG
Xdump_table()
X{
X	register int	i;
X
X	printf("%d local variables and parameters found\n", lcnt);
X	for (i=0; i < lcnt ;i++) {
X		printf("len = %d\n", locals[i].size);
X		printf("%02d: disp=%3ld, len=%d ref=%2d reg=%s",
X			i, locals[i].offset,
X			locals[i].size,
X			locals[i].ref,
X			locals[i].reg >= 0 ? masktos(RM(locals[i].reg)) : "-");
X		if (locals[i].flags & ADDR_TAKEN)
X			printf(" ADDR_TAKEN");
X		if (locals[i].flags & ALIASED)
X			printf(" ALIASED");
X		printf("\n");
X	}
X}
X#endif
END_OF_FILE
if test 17767 -ne `wc -c <'top/REG.C'`; then
    echo shar: \"'top/REG.C'\" unpacked with wrong size!
fi
# end of 'top/REG.C'
fi
echo shar: End of archive 8 \(of 9\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 9 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0