4.4BSD/usr/src/old/pcc/ccom.tahoe/local2.c

Compare this file to the similar file:
Show the results in this format:

#ifndef lint
static char sccsid[] = "@(#)local2.c	1.32 (Berkeley) 2/29/88";
#endif

# include "pass2.h"
# include <ctype.h>

# define putstr(s)	fputs((s), stdout)
# define ISCHAR(p)	(p->in.type == UCHAR || p->in.type == CHAR)

# ifdef FORT
int ftlab1, ftlab2;
# endif
/* a lot of the machine dependent parts of the second pass */

# define BITMASK(n) ((1L<<n)-1)

# ifndef ONEPASS
/*ARGSUSED*/
where(c){
	fprintf( stderr, "%s, line %d: ", filename, lineno );
	}
# endif

lineid( l, fn ) char *fn; {
	/* identify line l and file fn */
	printf( "#	line %d, file %s\n", l, fn );
	}

int ent_mask;

eobl2(){
	register OFFSZ spoff;	/* offset from stack pointer */
#ifndef FORT
	extern int ftlab1, ftlab2;
#endif

	spoff = maxoff;
	spoff /= SZCHAR;
	SETOFF(spoff,4);
#ifdef FORT
#ifndef FLEXNAMES
	printf( "	.set	.F%d,%ld\n", ftnno, spoff );
#else
	/* SHOULD BE L%d ... ftnno but must change pc/f77 */
	printf( "	.set	LF%d,%ld\n", ftnno, spoff );
#endif
	printf( "	.set	LWM%d,0x%x\n", ftnno, ent_mask&0x1ffc|0x1000);
#else
	printf( "	.set	L%d,0x%x\n", ftnno, ent_mask&0x1ffc);
	printf( "L%d:\n", ftlab1);
	if( maxoff > AUTOINIT )
		printf( "	subl3	$%ld,fp,sp\n", spoff);
	printf( "	jbr 	L%d\n", ftlab2);
#endif
	ent_mask = 0;
	maxargs = -1;
	}

struct hoptab { int opmask; char * opstring; } ioptab[] = {

	PLUS,	"add",
	MINUS,	"sub",
	MUL,	"mul",
	DIV,	"div",
	MOD,	"div",
	OR,	"or",
	ER,	"xor",
	AND,	"and",
	-1,	""    };

hopcode( f, o ){
	/* output the appropriate string from the above table */

	register struct hoptab *q;

	if(asgop(o))
		o = NOASG o;
	for( q = ioptab;  q->opmask>=0; ++q ){
		if( q->opmask == o ){
			if(f == 'E')
				printf( "e%s", q->opstring);
			else
				printf( "%s%c", q->opstring, tolower(f));
			return;
			}
		}
	cerror( "no hoptab for %s", opst[o] );
	}

char *
rnames[] = {  /* keyed to register number tokens */

	"r0", "r1",
	"r2", "r3", "r4", "r5",
	"r6", "r7", "r8", "r9", "r10", "r11",
	"r12", "fp", "sp", "pc",
	};

/* output register name and update entry mask */
char *
rname(r)
	register int r;
{

	if (!istreg(r))
		ent_mask |= 1<<r;
	return(rnames[r]);
}

int rstatus[] = {
	SAREG|STAREG, SAREG|STAREG,
	SAREG|STAREG, SAREG|STAREG, SAREG|STAREG, SAREG|STAREG,
	SAREG, SAREG, SAREG, SAREG, SAREG, SAREG,
	SAREG, SAREG, SAREG, SAREG,
	};

tlen(p) NODE *p;
{
	switch(p->in.type) {
		case CHAR:
		case UCHAR:
			return(1);

		case SHORT:
		case USHORT:
			return(SZSHORT/SZCHAR);

		case DOUBLE:
			return(SZDOUBLE/SZCHAR);

		default:
			return(SZINT/SZCHAR);
		}
}

mixtypes(p, q) NODE *p, *q;
{
	register TWORD tp, tq;

	tp = p->in.type;
	tq = q->in.type;

	return( (tp==FLOAT || tp==DOUBLE) !=
		(tq==FLOAT || tq==DOUBLE) );
}

prtype(n) NODE *n;
{
	switch (n->in.type)
		{

		case DOUBLE:
			putchar('d');
			return;

		case FLOAT:
			putchar('f');
			return;

		case LONG:
		case ULONG:
		case INT:
		case UNSIGNED:
			putchar('l');
			return;

		case SHORT:
		case USHORT:
			putchar('w');
			return;

		case CHAR:
		case UCHAR:
			putchar('b');
			return;

		default:
			if ( !ISPTR( n->in.type ) ) cerror("zzzcode- bad type");
			else {
				putchar('l');
				return;
				}
		}
}

zzzcode( p, c ) register NODE *p; {
	register int m;
	int val;
	switch( c ){

	case 'N':  /* logical ops, turned into 0-1 */
		/* use register given by register 1 */
		cbgen( 0, m=getlab(), 'I' );
		deflab( p->bn.label );
		printf( "	clrl	%s\n", rname(getlr( p, '1' )->tn.rval) );
		deflab( m );
		return;

	case 'P':
		cbgen( p->in.op, p->bn.label, c );
		return;

	case 'G':	/* i *= f; asgops with int lhs and float rhs */
		{
		register NODE *l, *r, *s;
		int lt, rt;

		l = p->in.left;
		r = p->in.right;
		s = talloc();
		rt = r->in.type;
		lt = l->in.type;

		if (lt != INT && lt != UNSIGNED) {
			s->in.op = SCONV;
			s->in.left = l;
			s->in.type = ISUNSIGNED(lt) ? UNSIGNED : INT;
			zzzcode(s, 'U');
			putstr("\n\t");
		}

		if (ISUNSIGNED(lt)) {
			s->in.op = SCONV;
			s->in.left = lt == UNSIGNED ? l : resc;
			s->in.type = rt;
			unsigned_to_float(s);
		} else {
			putstr("cvl");
			prtype(r);
			putchar('\t');
			adrput(lt == INT ? l : resc);
		}
		putstr("\n\t");

		hopcode(rt == FLOAT ? 'F' : 'D', p->in.op);
		putchar('\t');
		adrput(r);

		if (ISUNSIGNED(lt)) {
			putstr("\n\t");
			s->in.op = SCONV;
			s->in.left = r;		/* we need only the type */
			s->in.type = UNSIGNED;
			float_to_unsigned(s);
		} else {
			putstr("\n\tcv");
			prtype(r);
			putstr("l\t");
			if (lt == INT)
				adrput(l);
			else
				adrput(resc);
		}
		if (lt != INT) {
			putstr("\n\t");
			s->in.op = ASSIGN;
			s->in.left = l;
			s->in.right = resc;
			s->in.type = lt;
			zzzcode(s, 'U');
		}

		s->in.op = FREE;
		return;
		}

	case 'J':	/* unsigned DIV/MOD with constant divisors */
		{
		register int ck = INAREG;
		int label1, label2;

		/* case constant <= 1 is handled by optim() in pass 1 */
		/* case constant < 0x80000000 is handled in table */
		switch( p->in.op ) {
		/* case DIV: handled in optim2() */
		case MOD:
			if( p->in.left->in.op == REG &&
			    p->in.left->tn.rval == resc->tn.rval )
				goto asgmod;
			label1 = getlab();
			expand(p, ck, "movl\tAL,A1\n\tcmpl\tA1,AR\n");
			printf("\tjlssu\tL%d\n", label1);
			expand(p, ck, "\tsubl2\tAR,A1\n");
			printf("L%d:", label1);
			break;
		case ASG DIV:
			label1 = getlab();
			label2 = getlab();
			expand(p, ck, "cmpl\tAL,AR\n");
			printf("\tjgequ\tL%d\n", label1);
			expand(p, ck, "\tmovl\t$1,AL\n");
			printf("\tjbr\tL%d\nL%d:\n", label2, label1);
			expand(p, ck, "\tclrl\tAL\n");
			printf("L%d:", label2);
			break;
		case ASG MOD:
		asgmod:
			label1 = getlab();
			expand(p, ck, "cmpl\tAL,AR\n");
			printf("\tjlssu\tL%d\n", label1);
			expand(p, ck, "\tsubl2\tAR,AL\n");
			printf("L%d:", label1);
			break;
			}
		return;
		}

	case 'B':	/* get oreg value in temp register for shift */
		{
		register NODE *r;
		if (xdebug) eprint(p, 0, &val, &val);
		r = p->in.right;
		if( tlen(r) == SZINT/SZCHAR && r->in.type != FLOAT )
			putstr("movl");
		else {
			putstr(ISUNSIGNED(r->in.type) ? "movz" : "cvt");
			prtype(r);
			putchar('l');
			}
		return;
		}

	case 'C':	/* generate 'call[fs] $bytes' */
		{
		extern int gc_numbytes;
		extern int xdebug;

		if (xdebug) printf("->%d<-",gc_numbytes);

		printf("call%c	$%d",
		 (p->in.left->in.op==ICON && gc_numbytes<60)?'f':'s',
		 gc_numbytes+4);
		/* don't change to double (here's the only place to catch it) */
		if(p->in.type == FLOAT)
			rtyflg = 1;
		return;
		}

	case 'D':	/* INCR and DECR */
		if (p->in.left->in.type == FLOAT)
			expand(p, INAREG, "movl\tAL,A1");
		else if (p->in.left->in.type == DOUBLE)
			expand(p, INAREG, "ldd\tAL\n\tstd\tA1");
		else
			zzzcode(p->in.left, 'U');
		putstr("\n	");

	case 'E':	/* INCR and DECR, FOREFF */
 		if (p->in.right->in.op == ICON && p->in.right->tn.lval == 1)
			{
			putstr(p->in.op == INCR ? "inc" : "dec");
			prtype(p->in.left);
			putchar('\t');
			adrput(p->in.left);
			return;
			}
		else if (p->in.left->in.type == FLOAT || p->in.left->in.type == DOUBLE) {
			if (c == 'E' || p->in.left->in.type == FLOAT)
				expand(p, INAREG, "ldZL\tAL\n\t");
			if (p->in.op == INCR)
				expand(p, INAREG, "addZL\tAR\n\tstZL\tAL");
			else /* DECR */
				expand(p, INAREG, "subZL\tAR\n\tstZL\tAL");
			return;
			}
		putstr(p->in.op == INCR ? "add" : "sub");
		prtype(p->in.left);
		putstr("2	");
		adrput(p->in.right);
		putchar(',');
		adrput(p->in.left);
		return;

	case 'F':	/* masked constant for fields */
		printf(ACONFMT, (p->in.right->tn.lval&((1<<fldsz)-1))<<fldshf);
		return;

	case 'I':	/* produce value of bitfield assignment */
			/* avoid shifts -- shifts are SLOW on this machine */
			/* XXX this wouldn't be necessary if we were smarter
			       and masked BEFORE shifting XXX */
		{
		register NODE *r = p->in.right;
		if(r->in.op == ICON && r->tn.name[0] == '\0') {
			putstr("movl\t");
			printf(ACONFMT, r->tn.lval & ((1<<fldsz)-1));
			}
		else {
			putstr("andl3\t");
			printf(ACONFMT, (1 << fldsz) - 1);
			putchar(',');
			adrput(r);
			}
		putchar(',');
		adrput(resc);
		break;
		}

	case 'H':	/* opcode for shift */
		if(p->in.op == LS || p->in.op == ASG LS)
			putstr("shll");
		else if(ISUNSIGNED(p->in.left->in.type))
			putstr("shrl");
		else
			putstr("shar");
		return;

	case 'L':	/* type of left operand */
	case 'R':	/* type of right operand */
		{
		register NODE *n;
		extern int xdebug;

		n = getlr ( p, c);
		if (xdebug) printf("->%d<-", n->in.type);

		prtype(n);
		return;
		}

	case 'M': {  /* initiate ediv for mod and unsigned div */
		putstr("clrl\t");
		adrput(resc);
		putstr("\n\tmovl\t");
		adrput(p->in.left);
		putchar(',');
		upput(resc, SZLONG);
		printf("\n\tjgeq\tL%d\n\tmnegl\t$1,", m = getlab());
		adrput(resc);
		putchar('\n');
		deflab(m);
		return;
	}

	case 'T': {	/* rounded structure length for arguments */
		int size = p->stn.stsize;
		SETOFF( size, 4);
		printf("movab	-%d(sp),sp", size);
		return;
	}

	case 'S':  /* structure assignment */
		stasg(p);
		break;

#ifdef I_don_t_understand_this
	case 'X':	/* multiplication for short and char */
		if (ISUNSIGNED(p->in.left->in.type)) 
			printf("\tmovz");
		else
			printf("\tcvt");
		zzzcode(p, 'L');
		printf("l\t");
		adrput(p->in.left);
		printf(",");
		adrput(&resc[0]);
		printf("\n");
		if (ISUNSIGNED(p->in.right->in.type)) 
			printf("\tmovz");
		else
			printf("\tcvt");
		zzzcode(p, 'R');
		printf("l\t");
		adrput(p->in.right);
		printf(",");
		adrput(&resc[1]);
		printf("\n");
		return;
#endif

	case 'U':		/* SCONV */
	case 'V':		/* SCONV with FORCC */
		sconv(p, c == 'V');
		break;

	case 'W': {		/* SCONV or ASSIGN float/double => unsigned */
		NODE *src = p->in.op == SCONV ? p->in.left : p->in.right;

		putstr("ld");
		prtype(src);
		putchar('\t');
		adrput(src);
		putstr("\n\t");
		float_to_unsigned(p);
		break;
	}

	case 'Y':		/* SCONV or ASSIGN unsigned => float/double */
		unsigned_to_float(p);	/* stores into accumulator */
		putstr("\n\tst");
		prtype(p);
		putchar('\t');
		if (p->in.op == SCONV)
			adrput(resc);
		else
			adrput(p->in.left);
		rtyflg = 1;
		break;

#ifdef I_don_t_understand_this
	case 'Z':
		p = p->in.right;
		switch (p->in.type) {
		case SHORT: {
			short w = p->tn.lval;
			p->tn.lval = w;
			break;
		}
		case CHAR: {
			char c = p->tn.lval;
			p->tn.lval = c;
			break;
		}
		}
		printf("$%d", p->tn.lval);
		break;
#endif

	default:
		cerror( "illegal zzzcode" );
	}
}

#define	MOVB(dst, src, off) { \
	putstr("\tmovb\t"); upput(src, off); putchar(','); \
	upput(dst, off); putchar('\n'); \
}
#define	MOVW(dst, src, off) { \
	putstr("\tmovw\t"); upput(src, off); putchar(','); \
	upput(dst, off); putchar('\n'); \
}
#define	MOVL(dst, src, off) { \
	putstr("\tmovl\t"); upput(src, off); putchar(','); \
	upput(dst, off); putchar('\n'); \
}
/*
 * Generate code for a structure assignment.
 */
stasg(p)
	register NODE *p;
{
	register NODE *l, *r;
	register int size;

	switch (p->in.op) {
	case STASG:			/* regular assignment */
		l = p->in.left;
		r = p->in.right;
		break;
	case STARG:			/* place arg on the stack */
		l = getlr(p, '3');
		r = p->in.left;
		break;
	default:
		cerror("STASG bad");
		/*NOTREACHED*/
	}
	/*
	 * Pun source for use in code generation.
	 */
	switch (r->in.op) {
	case ICON:
		r->in.op = NAME;
		break;
	case REG:
		r->in.op = OREG;
		break;
	default:
		cerror( "STASG-r" );
		/*NOTREACHED*/
	}
	size = p->stn.stsize;
	if (size <= 0 || size > 65535)
		cerror("structure size out of range");
	/*
	 * Generate optimized code based on structure size
	 * and alignment properties....
	 */
	switch (size) {

	case 1:
		putstr("\tmovb\t");
	optimized:
		adrput(r);
		putchar(',');
		adrput(l);
		putchar('\n');
		break;

	case 2:
		if (p->stn.stalign != 2) {
			MOVB(l, r, SZCHAR);
			putstr("\tmovb\t");
		} else
			putstr("\tmovw\t");
		goto optimized;

	case 4:
		if (p->stn.stalign != 4) {
			if (p->stn.stalign != 2) {
				MOVB(l, r, 3*SZCHAR);
				MOVB(l, r, 2*SZCHAR);
				MOVB(l, r, 1*SZCHAR);
				putstr("\tmovb\t");
			} else {
				MOVW(l, r, SZSHORT);
				putstr("\tmovw\t");
			}
		} else
			putstr("\tmovl\t");
		goto optimized;

	case 6:
		if (p->stn.stalign != 2)
			goto movblk;
		MOVW(l, r, 2*SZSHORT);
		MOVW(l, r, 1*SZSHORT);
		putstr("\tmovw\t");
		goto optimized;

	case 8:
		if (p->stn.stalign == 4) {
			MOVL(l, r, SZLONG);
			putstr("\tmovl\t");
			goto optimized;
		}
		/* fall thru...*/

	default:
	movblk:
		/*
		 * Can we ever get a register conflict with R1 here?
		 */
		putstr("\tmovab\t");
		if(r->in.op == OREG && r->tn.rval == R1)
		{
			adrput(r);
			printf(",r0\n\tmovab\t");
			adrput(l);
			putstr(",r1\n");
		}
		else
		{
			adrput(l);
			putstr(",r1\n\tmovab\t");
			adrput(r);
			printf(",r0\n");
		}
		printf("\tmovl\t$%d,r2\n\tmovblk\n", size);
		rname(R2);
		break;
	}
	/*
	 * Reverse above pun for reclaim.
	 */
	if (r->in.op == NAME)
		r->in.op = ICON;
	else if (r->in.op == OREG)
		r->in.op = REG;
}

/*
 * Convert a float or double in the accumulator into an unsigned int.
 * Unlike the vax, the tahoe stores 0 into the destination
 *	on a conversion of > 2 ** 31, so we compensate.
 */
float_to_unsigned(p)
	NODE *p;
{
	register NODE *l = p->in.left;
	int label1 = getlab();
	int label2 = getlab();
	int label3 = getlab();
	NODE *src, *dst;

	if (p->in.op == SCONV) {
		src = p->in.left;
		dst = resc;
	} else {
		src = p->in.right;
		dst = p->in.left;
	}

	printf(".data\n\t.align\t2\nL%d:\n\t.long\t0x50000000", label1);
	if (src->in.type == DOUBLE)
		putstr(", 0x00000000 # .double");
	else
		putstr(" # .float");
	putstr(" 2147483648\n\t.text\n\tcmp");
	prtype(src);
	printf("\tL%d\n\tjlss\tL%d\n\tsub", label1, label2);
	prtype(src);
	printf("\tL%d\n\tcv", label1);
	prtype(src);
	putstr("l\t");
	adrput(dst);
	putstr("\n\taddl2\t$-2147483648,");
	adrput(dst);
	printf("\n\tjbr\tL%d\nL%d:\n\tcv", label3, label2);
	prtype(src);
	putstr("l\t");
	adrput(dst);
	printf("\nL%d:", label3);
}

/*
 * Convert an unsigned int into a float or double, leaving the result
 *	in the accumulator.
 */
unsigned_to_float(p)
	register NODE *p;
{
	int label1 = getlab();
	int label2 = getlab();
	NODE *src, *dst;

	if (p->in.op == SCONV) {
		src = p->in.left;
		dst = resc;
	} else {
		src = p->in.right;
		dst = p->in.left;
	}

	printf(".data\n\t.align\t2\nL%d:\n\t.long\t0x50800000", label2);
	if (p->in.type == DOUBLE)
		putstr(", 0x00000000 # .double");
	else
		putstr(" # .float");
	putstr(" 4294967296\n\t.text\n\tmovl\t");
	adrput(src);
	putchar(',');
	adrput(dst);
	putstr("\n\tcvl");
	prtype(p);
	putchar('\t');
	adrput(dst);
	printf("\n\tjgeq\tL%d\n\tadd", label1);
	prtype(p);
	printf("\tL%d\nL%d:", label2, label1);
}

/*
 * Prlen() is a cheap prtype()...
 */
static char convtab[SZINT/SZCHAR + 1] = {
	'?', 'b', 'w', '?', 'l'
};
#define	prlen(len)	putchar(convtab[len])


/*
 * Generate code for integral scalar conversions.
 * Some of this code is designed to work around a tahoe misfeature
 *	that causes sign- and zero- extension to be defeated in
 *	certain circumstances.
 * Basically if the source operand of a CVT or MOVZ instruction is
 *	shorter than the destination, and the source is a register
 *	or an immediate constant, sign- and zero- extension are
 *	ignored and the high bits of the source are copied.  (Note
 *	that zero-extension is not a problem for immediate
 *	constants.)
 * Another problem -- condition codes for a conversion with a
 *	register source reflect the source rather than the destination.
 */
sconv(p, forcc)
	NODE *p;
	int forcc;
{
	register NODE *src, *dst;
	register NODE *tmp;
	register int srclen, dstlen;
	int srctype, dsttype;
	int val;
	int neg = 0;

	if (p->in.op == ASSIGN) {
		src = p->in.right;
		dst = p->in.left;
		dstlen = tlen(dst);
		dsttype = dst->in.type;
	} else if (p->in.op == SCONV) {
		src = p->in.left;
		dst = resc;
		dstlen = tlen(p);
		dsttype = p->in.type;
	} else /* if (p->in.op == OPLEAF) */ {
		src = p;
		dst = resc;
		dstlen = SZINT/SZCHAR;
		dsttype = ISUNSIGNED(src->in.type) ? UNSIGNED : INT;
	}

	if (src->in.op == REG) {
		srclen = SZINT/SZCHAR;
		srctype = ISUNSIGNED(src->in.type) ? UNSIGNED : INT;
	} else {
		srclen = tlen(src);
		srctype = src->in.type;
	}

	if (src->in.op == ICON && src->tn.name[0] == '\0') {
		if (src->tn.lval == 0) {
			putstr("clr");
			prtype(dst);
			putchar('\t');
			adrput(dst);
			return;
		}
		if (dstlen < srclen) {
			switch (dsttype) {
			case CHAR:
				src->tn.lval = (char) src->tn.lval;
				break;
			case UCHAR:
				src->tn.lval = (unsigned char) src->tn.lval;
				break;
			case SHORT:
				src->tn.lval = (short) src->tn.lval;
				break;
			case USHORT:
				src->tn.lval = (unsigned short) src->tn.lval;
				break;
			}
		}
		if (dst->in.op == REG) {
			dsttype = INT;
			dstlen = SZINT/SZCHAR;
		}
		srctype = dsttype;
		srclen = dstlen;
		val = -src->tn.lval & ((1 << dstlen * SZCHAR) - 1);
		if ((unsigned) val < 64) {
			src->tn.lval = val;
			++neg;		/* MNEGx may be shorter */
		}
	}

	if (srclen < dstlen) {
		if (srctype == CHAR && dsttype == USHORT && dst->in.op == REG) {
			/* (unsigned short) c; => sign extend to 16 bits */
			putstr("cvtbl\t");
			adrput(src);
			putstr(",-(sp)\n\tmovzwl\t2(sp),");
			adrput(dst);
			putstr("\n\tmovab\t4(sp),sp");
			if (forcc) {
				putstr("\n\ttstl\t");
				adrput(dst);
			}
			return;
		}
		genconv(ISUNSIGNED(srctype),
			srclen, dst->in.op == REG ? SZINT/SZCHAR : dstlen,
			src, dst, forcc);
		return;
	}

	if (srclen > dstlen && dst->in.op == REG) {
		/* if dst is a register, the result must look like an int */
		if (src->in.op == REG) {
			if (ISUNSIGNED(dsttype)) {
				val = (1 << dstlen * SZCHAR) - 1;
				if (src->tn.rval == dst->tn.rval)
					/* conversion in place */
					printf("andl2\t$%ld,", val);
				else {
					printf("andl3\t$%ld,", val);
					adrput(src);
					putchar(',');
				}
				adrput(dst);
				return;
			}
			/*
			 * Sign extension in register can also be
			 * accomplished by shifts, but unfortunately
			 * shifts are extremely slow, due to the lack
			 * of a barrel shifter.
			 */
			putstr("pushl\t");
			adrput(src);
			putstr("\n\tcvt");
			prlen(dstlen);
			printf("l\t%d(sp),", SZINT/SZCHAR - dstlen);
			adrput(dst);
			putstr("\n\tmovab\t4(sp),sp");
			if (forcc) {
				putstr("\n\ttstl\t");
				adrput(dst);
			}
			return;
		}
		tmp = talloc();
		if ((src->in.op == NAME) ||
		    (src->in.op == UNARY MUL && src->in.left->in.op == ICON) ||
		    (src->in.op == OREG && !R2TEST(src->tn.rval))) {
			/* we can increment src's address & pun it */
			*tmp = *src;
			tmp->tn.lval += srclen - dstlen;
		} else {
			/* we must store src's address */
			*tmp = *dst;
			putstr("mova");
			prlen(srclen);
			putchar('\t');
			adrput(src);
			putchar(',');
			adrput(tmp);
			putstr("\n\t");
			tmp->tn.op = OREG;
			tmp->tn.lval = srclen - dstlen;
		}
		genconv(ISUNSIGNED(dsttype), dstlen, SZINT/SZCHAR, tmp, dst, forcc);
		tmp->in.op = FREE;
		return;
	}

	genconv(neg ? -1 : ISUNSIGNED(dsttype),
		srclen, dst->in.op == REG ? SZINT/SZCHAR : dstlen,
		src, dst, forcc);
}

genconv(srcflag, srclen, dstlen, src, dst, forcc)
	int srcflag;
	register int srclen, dstlen;
	NODE *src, *dst;
	int forcc;
{
	if (srclen != dstlen) {
		if (srcflag > 0 && srclen < dstlen)
			putstr("movz");
		else
			putstr("cvt");
		prlen(srclen);
	} else if (srcflag < 0)
		putstr("mneg");
	else
		putstr("mov");
	prlen(dstlen);
	putchar('\t');
	adrput(src);
	putchar(',');
	adrput(dst);

	/*
	 * This hack is made necessary by architecture problems
	 *	described above
	 */
	if (forcc && src->in.op == REG && srclen > dstlen) {
		putstr("\n\ttst");
		prlen(dstlen);
		putchar('\t');
		adrput(dst);
	}
}

rmove( rt, rs, t ) TWORD t; {
	printf( "	movl	%s,%s\n", rname(rs), rname(rt) );
	if(t==DOUBLE)
		printf( "	movl	%s,%s\n", rname(rs+1), rname(rt+1) );
	}

struct respref
respref[] = {
	INTAREG|INTBREG,	INTAREG|INTBREG,
	INAREG|INBREG,	INAREG|INBREG|SOREG|STARREG|STARNM|SNAME|SCON,
	INTEMP,	INTEMP,
	FORARG,	FORARG,
	INTEMP,	INTAREG|INAREG|INTBREG|INBREG|SOREG|STARREG|STARNM,
	0,	0 };

setregs(){ /* set up temporary registers */
	fregs = 6;	/* tbl- 6 free regs on Tahoe (0-5) */
	}

#ifndef szty
szty(t) TWORD t;{ /* size, in registers, needed to hold thing of type t */
	return(t==DOUBLE ? 2 : 1 );
	}
#endif

/*ARGSUSED*/
rewfld( p ) NODE *p; {
	return(1);
	}

/*ARGSUSED*/
callreg(p) NODE *p; {
	return( R0 );
	}

base( p ) register NODE *p; {
	register int o = p->in.op;

	if( o==ICON && p->in.name[0] != '\0' ) return( 100 ); /* ie no base reg */
	if( o==REG ) return( p->tn.rval );
    if( (o==PLUS || o==MINUS) && p->in.left->in.op == REG && p->in.right->in.op==ICON)
		return( p->in.left->tn.rval );
    if( o==OREG && !R2TEST(p->tn.rval) && (p->in.type==INT || p->in.type==UNSIGNED || ISPTR(p->in.type)) )
		return( p->tn.rval + 0200*1 );
	if( o==NAME ) return( 100 + 0200*1 );
	return( -1 );
	}

offset( p, tyl ) register NODE *p; int tyl; {

	if( tyl==1 &&
	    p->in.op==REG &&
	    (p->in.type==INT || p->in.type==UNSIGNED) )
		return( p->tn.rval );
	if( p->in.op==LS &&
	    p->in.left->in.op==REG &&
	    (p->in.left->in.type==INT || p->in.left->in.type==UNSIGNED) &&
	    p->in.right->in.op==ICON &&
	    p->in.right->in.name[0]=='\0' &&
	    (1<<p->in.right->tn.lval)==tyl)
		return( p->in.left->tn.rval );
	if( tyl==2 &&
	    p->in.op==PLUS &&
	    (p->in.left->in.type==INT || p->in.left->in.type==UNSIGNED) &&
	    p->in.left->in.op==REG &&
	    p->in.right->in.op==REG &&
	    p->in.left->tn.rval==p->in.right->tn.rval )
		return( p->in.left->tn.rval );
	return( -1 );
	}

makeor2( p, q, b, o) register NODE *p, *q; register int b, o; {
	register NODE *t;
	NODE *f;

	p->in.op = OREG;
	f = p->in.left; 	/* have to free this subtree later */

	/* init base */
	switch (q->in.op) {
		case ICON:
		case REG:
		case OREG:
		case NAME:
			t = q;
			break;

		case MINUS:
			q->in.right->tn.lval = -q->in.right->tn.lval;
		case PLUS:
			t = q->in.right;
			break;

		case UNARY MUL:
			t = q->in.left->in.left;
			break;

		default:
			cerror("illegal makeor2");
	}

	p->tn.lval = t->tn.lval;
#ifndef FLEXNAMES
	{
		register int i;
		for(i=0; i<NCHNAM; ++i)
			p->in.name[i] = t->in.name[i];
	}
#else
	p->in.name = t->in.name;
#endif

	/* init offset */
	p->tn.rval = R2PACK( (b & 0177), o, (b>>7) );

	tfree(f);
	return;
	}

canaddr( p ) NODE *p; {
	register int o = p->in.op;

	if( o==NAME || o==REG || o==ICON || o==OREG || (o==UNARY MUL && shumul(p->in.left)) ) return(1);
	return(0);
	}

#ifndef shltype
shltype( o, p ) register NODE *p; {
	return( o== REG || o == NAME || o == ICON || o == OREG || ( o==UNARY MUL && shumul(p->in.left)) );
	}
#endif

flshape( p ) NODE *p; {
	register int o = p->in.op;

	if( o==NAME || o==REG || o==ICON || o==OREG || (o==UNARY MUL && shumul(p->in.left)) ) return(1);
	return(0);
	}

/* INTEMP shapes must not contain any temporary registers */
shtemp( p ) register NODE *p; {
	int r;

	if( p->in.op == STARG ) p = p->in.left;

	switch (p->in.op) {
	case REG:
		return( !istreg(p->tn.rval) );
	case OREG:
		r = p->tn.rval;
		if( R2TEST(r) ) {
			if( istreg(R2UPK1(r)) )
				return(0);
			r = R2UPK2(r);
			}
		return( !istreg(r) );
	case UNARY MUL:
		p = p->in.left;
		return( p->in.op != UNARY MUL && shtemp(p) );
		}

	if( optype( p->in.op ) != LTYPE ) return(0);
	return(1);
	}

shumul( p ) register NODE *p; {
	register int o;
	extern int xdebug;

	if (xdebug) {
		 printf("\nshumul:op=%d,lop=%d,rop=%d", p->in.op, p->in.left->in.op, p->in.right->in.op);
		printf(" prname=%s,plty=%d, prlval=%D\n", p->in.right->in.name, p->in.left->in.type, p->in.right->tn.lval);
		}

	o = p->in.op;
	if(( o == NAME || (o == OREG && !R2TEST(p->tn.rval)) || o == ICON )
	 && p->in.type != PTR+DOUBLE)
		return( STARNM );

	return( 0 );
	}

special( p, shape ) register NODE *p; {
	if( shape==SIREG && p->in.op == OREG && R2TEST(p->tn.rval) ) return(1);
	else return(0);
}

adrcon( val ) CONSZ val; {
	printf(ACONFMT, val);
	}

conput( p ) register NODE *p; {
	switch( p->in.op ){

	case ICON:
		acon( p );
		return;

	case REG:
		putstr(rname(p->tn.rval));
		return;

	default:
		cerror( "illegal conput" );
		}
	}

/*ARGSUSED*/
insput( p ) NODE *p; {
	cerror( "insput" );
	}

/*
 * Output the address of the second item in the
 * pair pointed to by p.
 */
upput(p, size)
	register NODE *p;
{
	CONSZ save;

	if (p->in.op == FLD)
		p = p->in.left;
	switch (p->in.op) {

	case NAME:
	case OREG:
		save = p->tn.lval;
		p->tn.lval += size/SZCHAR;
		adrput(p);
		p->tn.lval = save;
		break;

	case REG:
		if (size == SZLONG) {
			putstr(rname(p->tn.rval+1));
			break;
		}
		/* fall thru... */

	default:
		cerror("illegal upper address op %s size %d",
		    opst[p->tn.op], size);
		/*NOTREACHED*/
	}
}

adrput( p ) register NODE *p; {
	register int r;
	/* output an address, with offsets, from p */

	if( p->in.op == FLD ){
		p = p->in.left;
		}
	switch( p->in.op ){

	case NAME:
		acon( p );
		return;

	case ICON:
		/* addressable value of the constant */
		putchar('$');
		acon( p );
		return;

	case REG:
		putstr(rname(p->tn.rval));
		if(p->in.type == DOUBLE)	/* for entry mask */
			(void) rname(p->tn.rval+1);
		return;

	case OREG:
		r = p->tn.rval;
		if( R2TEST(r) ){ /* double indexing */
			register int flags;

			flags = R2UPK3(r);
			if( flags & 1 ) putchar('*');
			if( p->tn.lval != 0 || p->in.name[0] != '\0' ) acon(p);
			if( R2UPK1(r) != 100) printf( "(%s)", rname(R2UPK1(r)) );
			printf( "[%s]", rname(R2UPK2(r)) );
			return;
			}
		if( r == FP && p->tn.lval > 0 ){  /* in the argument region */
			if( p->in.name[0] != '\0' ) werror( "bad arg temp" );
			printf( CONFMT, p->tn.lval );
			putstr( "(fp)" );
			return;
			}
		if( p->tn.lval != 0 || p->in.name[0] != '\0') acon( p );
		printf( "(%s)", rname(p->tn.rval) );
		return;

	case UNARY MUL:
		/* STARNM or STARREG found */
		if( tshape(p, STARNM) ) {
			putchar( '*' );
			adrput( p->in.left);
			}
		return;

	default:
		cerror( "illegal address" );
		return;

		}

	}

acon( p ) register NODE *p; { /* print out a constant */

	if( p->in.name[0] == '\0' )
		printf( CONFMT, p->tn.lval);
	else {
#ifndef FLEXNAMES
		printf( "%.8s", p->in.name );
#else
		putstr( p->in.name );
#endif
		if( p->tn.lval != 0 ) {
			putchar( '+' );
			printf( CONFMT, p->tn.lval );
			}
		}
	}

genscall( p, cookie ) register NODE *p; {
	/* structure valued call */
	return( gencall( p, cookie ) );
	}

genfcall( p, cookie ) register NODE *p; {
	register NODE *p1;
	register int m;
	static char *funcops[6] = {
		"sin", "cos", "sqrt", "exp", "log", "atan"
	};

	/* generate function opcodes */
	if(p->in.op==UNARY FORTCALL && p->in.type==FLOAT &&
	 (p1 = p->in.left)->in.op==ICON &&
	 p1->tn.lval==0 && p1->in.type==INCREF(FTN|FLOAT)) {
#ifdef FLEXNAMES
		p1->in.name++;
#else
		strcpy(p1->in.name, p1->in.name[1]);
#endif
		for(m=0; m<6; m++)
			if(!strcmp(p1->in.name, funcops[m]))
				break;
		if(m >= 6)
			uerror("no opcode for fortarn function %s", p1->in.name);
	} else
		uerror("illegal type of fortarn function");
	p1 = p->in.right;
	p->in.op = FORTCALL;
	if(!canaddr(p1))
		order( p1, INAREG|INBREG|SOREG|STARREG|STARNM );
	m = match( p, INTAREG|INTBREG );
	return(m != MDONE);
}

/* tbl */
int gc_numbytes;
/* tbl */

/*ARGSUSED*/
gencall( p, cookie ) register NODE *p; {
	/* generate the call given by p */
	register NODE *p1, *ptemp;
	register int temp, temp1;
	register int m;

	if( p->in.right ) temp = argsize( p->in.right );
	else temp = 0;

	if( p->in.op == STCALL || p->in.op == UNARY STCALL ){
		/* set aside room for structure return */

		if( p->stn.stsize > temp ) temp1 = p->stn.stsize;
		else temp1 = temp;
		}

	if( temp > maxargs ) maxargs = temp;
	SETOFF(temp1,4);

	if( p->in.right ){ /* make temp node, put offset in, and generate args */
		ptemp = talloc();
		ptemp->in.op = OREG;
		ptemp->tn.lval = -1;
		ptemp->tn.rval = SP;
#ifndef FLEXNAMES
		ptemp->in.name[0] = '\0';
#else
		ptemp->in.name = "";
#endif
		ptemp->in.rall = NOPREF;
		ptemp->in.su = 0;
		genargs( p->in.right, ptemp );
		ptemp->in.op = FREE;
		}

	p1 = p->in.left;
	if( p1->in.op != ICON ){
		if( p1->in.op != REG ){
			if( p1->in.op != OREG || R2TEST(p1->tn.rval) ){
				if( p1->in.op != NAME ){
					order( p1, INAREG );
					}
				}
			}
		}

/* tbl
	setup gc_numbytes so reference to ZC works */

	gc_numbytes = temp&(0x3ff);

	p->in.op = UNARY CALL;
	m = match( p, INTAREG|INTBREG );

	return(m != MDONE);
	}

/* tbl */
char *
ccbranches[] = {
	"eql",
	"neq",
	"leq",
	"lss",
	"geq",
	"gtr",
	"lequ",
	"lssu",
	"gequ",
	"gtru",
	};
/* tbl */

/*ARGSUSED*/
cbgen( o, lab, mode ) { /*   printf conditional and unconditional branches */

	if( o != 0 && ( o < EQ || o > UGT ) )
		cerror( "bad conditional branch: %s", opst[o] );
	printf( "	j%s	L%d\n", o == 0 ? "br" : ccbranches[o-EQ], lab );
	}

nextcook( p, cookie ) NODE *p; {
	/* we have failed to match p with cookie; try another */
	if( cookie == FORREW ) return( 0 );  /* hopeless! */
	if( !(cookie&(INTAREG|INTBREG)) ) return( INTAREG|INTBREG );
	if( !(cookie&INTEMP) && asgop(p->in.op) ) return( INTEMP|INAREG|INTAREG|INTBREG|INBREG );
	return( FORREW );
	}

/*ARGSUSED*/
lastchance( p, cook ) NODE *p; {
	/* forget it! */
	return(0);
	}

optim2( p ) register NODE *p; {
	/* do local tree transformations and optimizations */

	int o;
	int i, mask;
	register NODE *l, *r;

	switch( o = p->in.op ) {

	case ASG PLUS:
	case ASG MINUS:
	case ASG MUL:
	case ASG OR:
		/* simple ASG OPSIMP -- reduce range of constant rhs */
		l = p->in.left;
		r = p->in.right;
		if( tlen(l) < SZINT/SZCHAR &&
		    r->in.op==ICON && r->in.name[0]==0 ){
			mask = (1 << tlen(l) * SZCHAR) - 1;
			if( r->tn.lval & (mask & ~(mask >> 1)) )
				r->tn.lval |= ~mask;
			else
				r->tn.lval &= mask;
			}
		break;

	case AND:
	case ASG AND:
		r = p->in.right;
		if( r->in.op==ICON && r->in.name[0]==0 ) {
			/* check for degenerate operations */
			l = p->in.left;
			mask = (1 << tlen(l) * SZCHAR) - 1;
			if( o == ASG AND || ISUNSIGNED(r->in.type) ) {
				i = r->tn.lval & mask;
				if( i == mask ) {
					/* redundant mask */
					r->in.op = FREE;
					ncopy(p, l);
					l->in.op = FREE;
					break;
					}
				else if( i == 0 )
					/* all bits masked off */
					goto zero;
				r->tn.lval = i;
				if( tlen(l) < SZINT/SZCHAR ){
					/* sign extend */
					if( r->tn.lval & (mask & ~(mask >> 1)) )
						r->tn.lval |= ~mask;
					else
						r->tn.lval &= mask;
					}
				}
			else if( r->tn.lval == mask &&
				 tlen(l) < SZINT/SZCHAR ) {
				/* use movz instead of and */
				r->in.op = SCONV;
				r->in.left = l;
				r->in.right = 0;
				r->in.type = ENUNSIGN(l->in.type);
				r->in.su = l->in.su > 1 ? l->in.su : 1;
				ncopy(p, r);
				p->in.left = r;
				p->in.type = INT;
				}
			}
		break;

	case SCONV:
		l = p->in.left;
		if( p->in.type == FLOAT || p->in.type == DOUBLE ||
		    l->in.type == FLOAT || l->in.type == DOUBLE )
			return;
		if( l->in.op == PCONV )
			return;
		if( (l->in.op == CALL || l->in.op == UNARY CALL) &&
		    l->in.type != INT && l->in.type != UNSIGNED )
			return;

		/* Only trust it to get it right if the size is the same */
		if( tlen(p) != tlen(l) )
			return;

		/* clobber conversion */
		if( l->in.op != FLD )
			l->in.type = p->in.type;
		ncopy( p, l );
		l->in.op = FREE;

		break;

	case ASSIGN:
		/*
		 * Conversions are equivalent to assignments;
		 * when the two operations are combined,
		 * we can sometimes zap the conversion.
		 */
		r = p->in.right;
		l = p->in.left;
		if ( r->in.op == SCONV &&
		     !mixtypes(l, r) &&
		     l->in.op != FLD &&
		     tlen(l) == tlen(r) ) {
				p->in.right = r->in.left;
				r->in.op = FREE;
			}
		break;

	case ULE:
	case ULT:
	case UGE:
	case UGT:
		p->in.op -= (UGE-GE);
		if( degenerate(p) )
			break;
		p->in.op += (UGE-GE);
		break;

	case EQ:
	case NE:
	case LE:
	case LT:
	case GE:
	case GT:
		if( p->in.left->in.op == SCONV &&
		    p->in.right->in.op == SCONV ) {
			l = p->in.left;
			r = p->in.right;
			if( l->in.type == DOUBLE &&
			    l->in.left->in.type == FLOAT &&
			    r->in.left->in.type == FLOAT ) {
				/* nuke the conversions */
				p->in.left = l->in.left;
				p->in.right = r->in.left;
				l->in.op = FREE;
				r->in.op = FREE;
				}
			/* more? */
			}
		(void) degenerate(p);
		break;

	case DIV:
		if( p->in.right->in.op == ICON &&
		    p->in.right->tn.name[0] == '\0' &&
		    ISUNSIGNED(p->in.right->in.type) &&
		    (unsigned) p->in.right->tn.lval >= 0x80000000 ) {
			/* easy to do here, harder to do in zzzcode() */
			p->in.op = UGE;
			break;
			}
	case MOD:
	case ASG DIV:
	case ASG MOD:
		/*
		 * optimize DIV and MOD
		 *
		 * basically we spot UCHAR and USHORT and try to do them
		 * as signed ints...  this may need tuning for the tahoe.
		 */
		if( degenerate(p) )
			break;
		l = p->in.left;
		r = p->in.right;
		if( !ISUNSIGNED(r->in.type) ||
		    tlen(l) >= SZINT/SZCHAR ||
		    !(tlen(r) < SZINT/SZCHAR ||
		      (r->in.op == ICON && r->tn.name[0] == '\0')) )
			break;
		if( r->in.op == ICON )
			r->tn.type = INT;
		else {
			NODE *t = talloc();
			t->in.left = r;
			r = t;
			r->in.op = SCONV;
			r->in.type = INT;
			r->in.right = 0;
			p->in.right = r;
			}
		if( o == DIV || o == MOD ) {
			NODE *t = talloc();
			t->in.left = l;
			l = t;
			l->in.op = SCONV;
			l->in.type = INT;
			l->in.right = 0;
			p->in.left = l;
			}
		/* handle asgops in table */
		break;

	case RS:
	case ASG RS:
	case LS:
	case ASG LS:
		/* pick up degenerate shifts */
		l = p->in.left;
		r = p->in.right;
		if( !(r->in.op == ICON && r->tn.name[0] == '\0') )
			break;
		i = r->tn.lval;
		if( i < 0 )
			/* front end 'fixes' this? */
			if( o == LS || o == ASG LS )
				o += (RS-LS);
			else
				o += (LS-RS);
		if( (o == RS || o == ASG RS) &&
		    !ISUNSIGNED(l->in.type) )
			/* can't optimize signed right shifts */
			break;
		if( o == LS ) {
			if( i < SZINT )
				break;
			}
		else {
			if( i < tlen(l) * SZCHAR )
				break;
			}
	zero:
		if( !asgop( o ) )
			if( tshape(l, SAREG|SNAME|SCON|SOREG|STARNM) ) {
				/* no side effects */
				tfree(l);
				ncopy(p, r);
				r->in.op = FREE;
				p->tn.lval = 0;
				}
			else {
				p->in.op = COMOP;
				r->tn.lval = 0;
				}
		else {
			p->in.op = ASSIGN;
			r->tn.lval = 0;
			}
		break;
		}
	}

degenerate(p) register NODE *p; {
	int o;
	int result, i;
	int lower, upper;
	register NODE *l, *r;

	/*
	 * try to keep degenerate comparisons with constants
	 * out of the table.
	 */
	r = p->in.right;
	l = p->in.left;
	if( r->in.op != ICON ||
	    r->tn.name[0] != '\0' ||
	    tlen(l) >= tlen(r) )
		return (0);
	switch( l->in.type ) {
	case CHAR:
		lower = -(1 << SZCHAR - 1);
		upper = (1 << SZCHAR - 1) - 1;
		break;
	case UCHAR:
		lower = 0;
		upper = (1 << SZCHAR) - 1;
		break;
	case SHORT:
		lower = -(1 << SZSHORT - 1);
		upper = (1 << SZSHORT - 1) - 1;
		break;
	case USHORT:
		lower = 0;
		upper = (1 << SZSHORT) - 1;
		break;
	default:
		cerror("unsupported type in degenerate()");
		}
	i = r->tn.lval;
	switch( o = p->in.op ) {
	case DIV:
	case ASG DIV:
	case MOD:
	case ASG MOD:
		/* DIV and MOD work like EQ */
	case EQ:
	case NE:
		if( lower == 0 && (unsigned) i > upper )
			result = o == NE;
		else if( i < lower || i > upper )
			result = o == NE;
		else
			return (0);
		break;
	case LT:
	case GE:
		if( lower == 0 && (unsigned) i > upper )
			result = o == LT;
		else if( i <= lower )
			result = o != LT;
		else if( i > upper )
			result = o == LT;
		else
			return (0);
		break;
	case LE:
	case GT:
		if( lower == 0 && (unsigned) i >= upper )
			result = o == LE;
		else if( i < lower )
			result = o != LE;
		else if( i >= upper )
			result = o == LE;
		else
			return (0);
		break;
	default:
		cerror("unknown op in degenerate()");
		}
		
	if( o == MOD || o == ASG MOD ) {
		r->in.op = FREE;
		ncopy(p, l);
		l->in.op = FREE;
		}
	else if( o != ASG DIV && tshape(l, SAREG|SNAME|SCON|SOREG|STARNM) ) {
		/* no side effects */
		tfree(l);
		ncopy(p, r);
		r->in.op = FREE;
		p->tn.lval = result;
		}
	else {
		if( o == ASG DIV )
			p->in.op = ASSIGN;
		else {
			p->in.op = COMOP;
			r->tn.type = INT;
			}
		r->tn.lval = result;
		}
	if( logop(o) )
		p->in.type = INT;

	return (1);
	}

struct functbl {
	int fop;
	TWORD ftype;
	char *func;
	} opfunc[] = {
	DIV,		TANY,	"udiv",
	MOD,		TANY,	"urem",
	ASG DIV,	TANY,	"audiv",
	ASG MOD,	TANY,	"aurem",
	0,	0,	0 };

hardops(p)  register NODE *p; {
	/* change hard to do operators into function calls.  */
	register NODE *q;
	register struct functbl *f;
	register o;
	NODE *old,*temp;

	o = p->in.op;
	if( ! (optype(o)==BITYPE &&
	       (ISUNSIGNED(p->in.left->in.type) ||
		ISUNSIGNED(p->in.right->in.type))) )
		return;

	for( f=opfunc; f->fop; f++ ) {
		if( o==f->fop ) goto convert;
		}
	return;

	convert:
	if( p->in.right->in.op == ICON && p->in.right->tn.name[0] == '\0' )
		/* 'J' in zzzcode() -- assumes DIV or MOD operations */
		/* save a subroutine call -- use at most 5 instructions */
		return;
	if( tlen(p->in.left) < SZINT/SZCHAR && tlen(p->in.right) < SZINT/SZCHAR )
		/* optim2() will modify the op into an ordinary int op */
		return;
	if( asgop( o ) ) {
		old = NIL;
		switch( p->in.left->in.op ){
		case FLD:
			q = p->in.left->in.left;
			/*
			 * rewrite (lval.fld /= rval); as
			 *  ((*temp).fld = udiv((*(temp = &lval)).fld,rval));
			 * else the compiler will evaluate lval twice.
			 */
			if( q->in.op == UNARY MUL ){
				/* first allocate a temp storage */
				temp = talloc();
				temp->in.op = OREG;
				temp->tn.rval = TMPREG;
				temp->tn.lval = BITOOR(freetemp(1));
				temp->in.type = INCREF(p->in.type);
#ifdef FLEXNAMES
				temp->in.name = "";
#else
				temp->in.name[0] = '\0';
#endif
				old = q->in.left;
				q->in.left = temp;
			}
			/* fall thru ... */

		case REG:
		case NAME:
		case OREG:
			/* change ASG OP to a simple OP */
			q = talloc();
			q->in.op = NOASG p->in.op;
			q->in.rall = NOPREF;
			q->in.type = p->in.type;
			q->in.left = tcopy(p->in.left);
			q->in.right = p->in.right;
			p->in.op = ASSIGN;
			p->in.right = q;
			p = q;
			f -= 2; /* Note: this depends on the table order */
			/* on the right side only - replace *temp with
			 *(temp = &lval), build the assignment node */
			if( old ){
				temp = q->in.left->in.left; /* the "*" node */
				q = talloc();
				q->in.op = ASSIGN;
				q->in.left = temp->in.left;
				q->in.right = old;
				q->in.type = old->in.type;
#ifdef FLEXNAMES
				q->in.name = "";
#else
				q->in.name[0] = '\0';
#endif
				temp->in.left = q;
			}
			break;

		case UNARY MUL:
			/* avoid doing side effects twice */
			q = p->in.left;
			p->in.left = q->in.left;
			q->in.op = FREE;
			break;

		default:
			cerror( "hardops: can't compute & LHS" );
			}
		}

	/* build comma op for args to function */
	q = talloc();
	q->in.op = CM;
	q->in.rall = NOPREF;
	q->in.type = INT;
	q->in.left = p->in.left;
	q->in.right = p->in.right;
	p->in.op = CALL;
	p->in.right = q;

	/* put function name in left node of call */
	p->in.left = q = talloc();
	q->in.op = ICON;
	q->in.rall = NOPREF;
	q->in.type = INCREF( FTN + p->in.type );
#ifndef FLEXNAMES
	strcpy( q->in.name, f->func );
#else
	q->in.name = f->func;
#endif
	q->tn.lval = 0;
	q->tn.rval = 0;

	}

zappost(p) NODE *p; {
	/* look for ++ and -- operators and remove them */

	register int o, ty;
	register NODE *q;
	o = p->in.op;
	ty = optype( o );

	switch( o ){

	case INCR:
	case DECR:
			q = p->in.left;
			p->in.right->in.op = FREE;  /* zap constant */
			ncopy( p, q );
			q->in.op = FREE;
			return;

		}

	if( ty == BITYPE ) zappost( p->in.right );
	if( ty != LTYPE ) zappost( p->in.left );
}

fixpre(p) NODE *p; {

	register int o, ty;
	o = p->in.op;
	ty = optype( o );

	switch( o ){

	case ASG PLUS:
			p->in.op = PLUS;
			break;
	case ASG MINUS:
			p->in.op = MINUS;
			break;
		}

	if( ty == BITYPE ) fixpre( p->in.right );
	if( ty != LTYPE ) fixpre( p->in.left );
}

/*ARGSUSED*/
NODE * addroreg(l) NODE *l;
				/* OREG was built in clocal()
				 * for an auto or formal parameter
				 * now its address is being taken
				 * local code must unwind it
				 * back to PLUS/MINUS REG ICON
				 * according to local conventions
				 */
{
	cerror("address of OREG taken");
	/*NOTREACHED*/
}

# ifndef ONEPASS
main( argc, argv ) char *argv[]; {
	return( mainp2( argc, argv ) );
	}
# endif

strip(p) register NODE *p; {
	NODE *q;

	/* strip nodes off the top when no side effects occur */
	for( ; ; ) {
		switch( p->in.op ) {
		case SCONV:			/* remove lint tidbits */
			q = p->in.left;
			ncopy( p, q );
			q->in.op = FREE;
			break;
		/* could probably add a few more here */
		default:
			return;
			}
		}
	}

myreader(p) register NODE *p; {
	strip( p );		/* strip off operations with no side effects */
	canon( p );		/* expands r-vals for fields */
	walkf( p, hardops );	/* convert ops to function calls */
	walkf( p, optim2 );
	}